Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
This document is not UTF8. It was detected as ISO-8859-1 (Latin 1) and converted to UTF8 for display.
diff --git a/Makefile.am b/Makefile.am
index 567fccdd3..68b203a2e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,61 +1,61 @@
## Process this file with automake to produce Makefile.in
SUBDIRS = intl po zlib util mpi cipher tools g10 doc checks
EXTRA_DIST = VERSION PROJECTS
all-am: g10defs.h
all-recursive-am: g10defs.h
g10defs.h : config.h
@( set -e; \
echo "/* Generated automatically by Makefile */" ; \
echo "#ifdef __MINGW32__"; \
echo "#define G10_LOCALEDIR \"c:/lib/gnupg/locale\""; \
echo "#define GNUPG_LIBDIR \"c:/lib/gnupg\""; \
echo "#define GNUPG_DATADIR \"c:/lib/gnupg\""; \
echo "#else";\
echo "#define G10_LOCALEDIR \"$(prefix)/$(DATADIRNAME)/locale\""; \
echo "#define GNUPG_LIBDIR \"$(libdir)/gnupg\""; \
echo "#define GNUPG_DATADIR \"$(datadir)/gnupg\""; \
echo "#endif";\
) >g10defs.h
dist-hook:
@set -e; \
for file in `cd $(top_srcdir); find . -type f -name distfiles`; do \
dir=`dirname $$file` ; $(mkinstalldirs) $(distdir)/$$dir ; \
for i in distfiles `cat $(top_srcdir)/$$file` ; do \
ln $(top_srcdir)/$$dir/$$i $(distdir)/$$dir/$$i 2> /dev/null \
|| cp -p $(top_srcdir)/$$dir/$$i $(distdir)/$$dir/$$i; \
done ; \
done
@set -e; \
sed -e 's/@pkg_version@/$(VERSION)/g' $(top_srcdir)/scripts/gnupg.spec \
> $(distdir)/scripts/gnupg-$(VERSION).spec
# maintainer only
cvs-get:
rsync -Cavuzb --exclude scratch --exclude .deps \
koch@ftp.guug.de:work/gnupg .
cvs-put:
rsync -Cavuzb --exclude .deps --exclude scratch \
. koch@ftp.guug.de:work/gnupg
cvs-sync: cvs-get cvs-put
tobold-get:
rsync -Cavuzb --exclude scratch --exclude .deps \
tobold:gnupg/ .
tobold-put:
- rsync -Cavuzb --excude scratch --exclude .deps \
+ rsync -Cavuzb --exclude scratch --exclude .deps \
. tobold:gnupg/
.PHONY: cvs-get cvs-put cvs-sync
diff --git a/NEWS b/NEWS
index 17d0330e4..e1948f609 100644
--- a/NEWS
+++ b/NEWS
@@ -1,542 +1,544 @@
* 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:<name-of-gdbm-file>". 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 execption that you can't mix options and normal arguments.
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 accdently 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 availabe 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 specifiy 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/trustd.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 passward 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 vor vaious 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 implemenation.
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 implictly
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 libray.
* 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 diigest 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 supkeys
* 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, netxt 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 everthing 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 perfomance.
* 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 memeory 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/PROJECTS b/PROJECTS
index 10afb9976..47ed1eb93 100644
--- a/PROJECTS
+++ b/PROJECTS
@@ -1,10 +1,14 @@
* abstraction of the MPI
* Add a way to override the current cipher/md implementations
by others (using extensions)
* add a fast-import command which does not do the signature checks
of other keys (processing of the sdir hintlist). The signatures
may then be verified by a maintainence pass.
+ * Not GnupG replated: What about option completion in bash?
+ Can "--dump-options" be used for this or should we place the
+ options in a special ELF segment?
+
diff --git a/THANKS b/THANKS
index 88e7c2311..be08ddec1 100644
--- a/THANKS
+++ b/THANKS
@@ -1,72 +1,76 @@
GNUPG was originally written by Werner Koch. Other people contributed by
reporting problems, suggesting various improvements or submitting actual
code. Here is a list of those people. Help me keep it complete and free of
errors.
Anand Kumria wildfire@progsoc.uts.edu.au
Ariel T Glenn ariel@columbia.edu
Brian Moore bem@cmc.net
Brian Warner warner@lothar.com
Caskey L. Dickson caskey@technocage.com
Charles Levert charles@comm.polymtl.ca
Christian von Roques roques@pond.sub.org
Christopher Oliver oliver@fritz.traverse.net
Daniel Eisenbud eisenbud@cs.swarthmore.edu
+David Ellement ellement@sdd.hp.com
Detlef Lannert lannert@lannert.rz.uni-duesseldorf.de
Ed Boraas ecxjo@esperanto.org
Ernst Molitor ernst.molitor@uni-bonn.de
+Fabio Coatti cova@felix.unife.it
Frank Heckenbach heckenb@mi.uni-erlangen.de
Gaël Quéri gqueri@mail.dotcom.fr
Greg Louis glouis@dynamicro.on.ca
Gregory Steuck steuck@iname.com
Hendrik Buschkamp buschkamp@rheumanet.org
Holger Schurig holger@d.om.org
Hugh Daniel hugh@toad.com
Ian McKellar imckellar@harvestroad.com.au
James Troup james@nocrew.org
Jean-loup Gailly gzip@prep.ai.mit.edu
Jens Bachem bachem@rrz.uni-koeln.de
John A. Martin jam@jamux.com
Jörg Schilling schilling@fokus.gmd.de
Jun Kuriyama kuriyama@sky.rim.or.jp
Karl Fogel kfogel@guanabana.onshore.com
Karsten Thygesen karthy@kom.auc.dk
Kazu Yamamoto kazu@iijlab.net
Lars Kellogg-Stedman lars@bu.edu
Marco d'Itri md@linux.it
Mark Adler madler@alumni.caltech.edu
Markus Friedl Markus.Friedl@informatik.uni-erlangen.de
Martin Schulte schulte@thp.uni-koeln.de
Matthew Skala mskala@ansuz.sooke.bc.ca
Max Valianskiy maxcom@maxcom.ml.org
Michael Roth mroth@nessie.de
Michael Sobolev mss@despair.transas.com
Nicolas Graner Nicolas.Graner@cri.u-psud.fr
Niklas Hernaeus [Please don't spam him]
Nimrod Zimerman zimerman@forfree.at
Oskari Jääskeläinen f33003a@cc.hut.fi
Paul D. Smith psmith@baynetworks.com
Peter Gutmann pgut001@cs.auckland.ac.nz
QingLong qinglong@bolizm.ihep.su
Ralph Gillen gillen@theochem.uni-duesseldorf.de
+Roddy Strachan roddy@satlink.com.au
Roland Rosenfeld roland@spinnaker.rhein.de
Serge Munhoven munhoven@mema.ucl.ac.be
SL Baur steve@xemacs.org
+Stefan Karrmann S.Karrmann@gmx.net
Steffen Ullrich ccrlphr@xensei.com
Steffen Zahn zahn@berlin.snafu.de
Thomas Roessler roessler@guug.de
Tom Spindler dogcow@home.merit.edu
Tom Zerucha tzeruch@ceddec.com
Tomas Fasth tomas.fasth@twinspot.net
Thomas Mikkelsen tbm@image.dk
Ulf Möller 3umoelle@informatik.uni-hamburg.de
Walter Koch walterk@ddorf.rhein-ruhr.de
Werner Koch werner.koch@guug.de
Wim Vandeputte bunbun@reptile.rug.ac.be
nbecker@hns.com
Thanks to the German Unix User Group for providing FTP space and
Martin Hamilton for hosting the mailing list.
Many thanks to my wife Gerlinde for having so much patience with
me while hacking late in the evening.
diff --git a/TODO b/TODO
index e891645ae..bd5e43f21 100644
--- a/TODO
+++ b/TODO
@@ -1,69 +1,97 @@
+ * rmove assert in random.c:160 4096 bit keys need more random.
+
+ * list all matching user ids
+
+ * use zlib 1.1.13 to avoid a bug with 13 bit windows
+
+ * FreeBSD:
+#define USE_DYNAMIC_LINKING
+#define HAVE_DL_DLOPEN
+
+and the ld option -export-dynamic.
+
+ *
+Well, there was one thing extra I needed to do. While configure found the
+gdbm library, it didn't add the path the the include file list. I had to
+re-run configure with CPPFLAGS set:
+
+ env CPPFLAGS='-I/usr/local/include' configure ...
+
+
+ * clearsig: keep lineendings as they are. Remember that trailings
+ blanks are not hashed.
+
* Fix ;) revocation and expire stuff.
* check preferences (cipher and compress)
* OpenBSD: dynamic loading with dlopen works on OpenBSD, but:
OpenBSD binaries are a.out, so every symbol begins with "_"
* should we flush the getkey.c caches while doing an import?
* prefer a type 16 subkey for encryption because pgp cannot handle
type 20?
- * calculation of marginals never yields a completely trusted key.
-
* The critical bit of signature subpackets is not yet supported; i.e.
it is ignored.
- * Exportable Certification Flag is ignored
-
* We need a maintainence pass over the trustdb which flags
signatures as expired if the key used to make the signature has
expired. Maybe it is a good idea to store the exiration time
in the key record of the trustdb.
* write a tool to extract selected keys from a file.
* new menu to delete signatures and list signature in menu
* commandline controlled --edit-key with assumed save.
* -rdynamic auf Solaris Problem
* Replace the SIGUSR1 stuff by semaphores to avoid loss of a signal.
* add test cases for invalid data (scrambled armor or other random data)
* add some sanity checks to read_keyblock, so that we are sure that
the minimal requirements are met (?)
* decryption of message with multiple recipients does not work.
* preferences of hash algorithms are not yet used.
* rewrite --list-packets or put it into another tool.
* Burn the buffers used by fopen(), or use read(2). Does this
really make sense?
* Change the buffering to a mbuf like scheme? Need it for PSST anyway.
* add checking of armor trailers
* remove all "Fixmes"
* Change the internal represention of keyid into a struct which
can also hold the localid and extend the localid to hold information
of the subkey number because two subkeys may have the same keyid.
* add an option to re-create a public key from a secret key; we
can do this in trustdb.c:verify_own_keys.
+ * signature verification is done duplicated on import: in import.c and
+ tehn in trustdb.c too. Maybe we can use a flag to skip the actual
+ verification process (this should work if we use the same keyblock,
+ but I'm not sure how to accomplish that). Another way is to allow
+ the import of bogus data and let trustdb mark these keys as invalid;
+ I see an advantage in this that it may help to prevent a DoS on a
+ keyserver by sending him a lot of bogus signatures which he has
+ to check - Needs further investigation.
+
* change the fake_data stuff to mpi_set_opaque
* Is it okay to use gettext for the help system?
* Add some stuff for DU cc
* use "passphrase" instead of "pass phrase"
* Use "user ID", "trustdb" and "WARNING".
diff --git a/configure.in b/configure.in
index a497b3da9..29a439581 100644
--- a/configure.in
+++ b/configure.in
@@ -1,288 +1,288 @@
dnl
dnl Configure template for GNUPG
dnl
dnl (Process this file with autoconf to produce a configure script.)
dnl Must reset CDPATH so that bash's cd does not print to stdout
CDPATH=
AC_INIT(g10/g10.c)
AC_CONFIG_AUX_DIR(scripts)
AM_CONFIG_HEADER(config.h)
VERSION=`cat $srcdir/VERSION`
PACKAGE=gnupg
ALL_LINGUAS="en de it fr ru"
AC_SUBST(VERSION)
AC_SUBST(PACKAGE)
AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE")
AC_ARG_ENABLE(dev-random,
[ --disable-dev-random disable the use of dev random],
try_dev_random=$enableval, try_dev_random=yes)
AC_ARG_ENABLE(dynload,
[ --disable-dynload disable dynamic loading],
try_dynload=$enableval, try_dynload=yes)
AC_MSG_CHECKING([whether memory debugging is requested])
AC_ARG_ENABLE(m-debug,
[ --enable-m-debug enable debugging of memory allocation],
use_m_debug=$enableval, use_m_debug=no)
AC_MSG_RESULT($use_m_debug)
if test "$use_m_debug" = yes; then
AC_DEFINE(M_DEBUG)
use_m_guard=yes
else
AC_MSG_CHECKING([whether memory guard is requested])
AC_ARG_ENABLE(m-guard,
[ --disable-m-guard disable memory guard facility],
use_m_guard=$enableval, use_m_guard=yes)
AC_MSG_RESULT($use_m_guard)
fi
if test "$use_m_guard" = yes ; then
AC_DEFINE(M_GUARD)
- CFLAGS="-g -Wall"
+ CFLAGS="$CFLAGS -g -Wall"
else
- CFLAGS="-O2 -Wall"
+ CFLAGS="$CFLAGS -O2 -Wall"
fi
AC_MSG_CHECKING([whether included zlib is requested])
AC_ARG_WITH(included-zlib,
[ --with-included-zlib use the zlib code included here],
[g10_force_zlib=yes], [g10_force_zlib=no] )
AC_MSG_RESULT($g10_force_zlib)
dnl Checks for programs.
AC_CANONICAL_SYSTEM
AC_ARG_PROGRAM
AC_PROG_MAKE_SET
AM_SANITY_CHECK
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
dnl AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AC_PROG_CC
AC_PROG_CPP
AC_ISC_POSIX
AC_PROG_RANLIB
AC_PROG_INSTALL
AM_CYGWIN32
case "${target}" in
i386--mingw32)
# special stuff for Windoze NT
cross_compiling=yes
CC="i386--mingw32-gcc"
CPP="i386--mingw32-gcc -E"
RANLIB="i386--mingw32-ranlib"
ac_cv_have_dev_random=no
AC_DEFINE(USE_RAND_W32)
;;
*-*-hpux*)
if test -z "$GCC" ; then
CFLAGS="$CFLAGS -Ae -D_HPUX_SOURCE"
fi
AC_DEFINE(USE_RAND_UNIX)
;;
*)
AC_DEFINE(USE_RAND_UNIX)
;;
esac
case "${target}" in
i386--mingw32)
PRINTABLE_OS_NAME="MingW32"
;;
*-linux*)
PRINTABLE_OS_NAME="GNU/Linux"
;;
*)
PRINTABLE_OS_NAME=`uname -s || echo "Unknown"`
;;
esac
AC_DEFINE_UNQUOTED(PRINTABLE_OS_NAME, "$PRINTABLE_OS_NAME")
dnl Fixme: Are these the best flags for OpenBSD????
case "${target}" in
*-openbsd*)
NAME_OF_DEV_RANDOM="/dev/srandom"
NAME_OF_DEV_URANDOM="/dev/urandom"
DYNLINK_MOD_CFLAGS="-shared -rdynamic -fpic -Wl,-Bshareable -Wl,-x"
;;
*)
NAME_OF_DEV_RANDOM="/dev/random"
NAME_OF_DEV_URANDOM="/dev/urandom"
DYNLINK_MOD_CFLAGS="-shared -fPIC -lc"
;;
esac
AC_DEFINE_UNQUOTED(NAME_OF_DEV_RANDOM, "$NAME_OF_DEV_RANDOM")
AC_DEFINE_UNQUOTED(NAME_OF_DEV_URANDOM, "$NAME_OF_DEV_URANDOM")
dnl Checks for libraries.
AM_GNU_GETTEXT
AC_CHECK_LIB(gdbm,gdbm_firstkey)
if test "$try_dynload" = yes ; then
AC_CHECK_LIB(dl,dlopen)
if test "$ac_cv_lib_dl_dlopen" = "yes"; then
AC_DEFINE(USE_DYNAMIC_LINKING)
AC_DEFINE(HAVE_DL_DLOPEN)
DYNLINK_LDFLAGS=-rdynamic
use_gnupg_extensions=yes
else
AC_CHECK_LIB(dld,dld_link)
if test "$ac_cv_lib_dld_dld_link" = "yes"; then
AC_DEFINE(USE_DYNAMIC_LINKING)
AC_DEFINE(HAVE_DLD_DLD_LINK)
DYNLINK_LDFLAGS=-rdynamic
use_gnupg_extensions=yes
fi
fi
else
AC_MSG_CHECKING(for dynamic loading)
DYNLINK_LDFLAGS=
DYNLINK_MOD_CFLAGS=
use_gnupg_extensions=no
AC_MSG_RESULT(has been disabled)
fi
AM_CONDITIONAL(ENABLE_GNUPG_EXTENSIONS, test "$use_gnupg_extensions" = yes )
AC_SUBST(DYNLINK_LDFLAGS)
AC_SUBST(DYNLINK_MOD_CFLAGS)
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(unistd.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_SIGNAL
AC_DECL_SYS_SIGLIST
WK_CHECK_ENDIAN
WK_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF)
WK_CHECK_TYPEDEF(ushort, HAVE_USHORT_TYPEDEF)
WK_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF)
WK_CHECK_TYPEDEF(u16, HAVE_U16_TYPEDEF)
WK_CHECK_TYPEDEF(u32, HAVE_U32_TYPEDEF)
AC_CHECK_SIZEOF(unsigned short, 2)
AC_CHECK_SIZEOF(unsigned int, 4)
AC_CHECK_SIZEOF(unsigned long, 4)
if test "$ac_cv_sizeof_unsigned_short" = "0" \
|| test "$ac_cv_sizeof_unsigned_int" = "0" \
|| test "$ac_cv_sizeof_unsigned_long" = "0"; then
AC_MSG_WARN([Hmmm, something is wrong with the sizes - using defaults]);
fi
dnl Checks for library functions.
AC_FUNC_VPRINTF
AC_CHECK_FUNCS(strerror stpcpy strlwr tcgetattr rand strtoul mmap)
AC_CHECK_FUNCS(memmove gettimeofday getrusage gethrtime setrlimit)
AC_CHECK_FUNCS(memicmp atexit raise getpagesize strftime)
WK_CHECK_MLOCK
WK_CHECK_IPC
if test "$ac_cv_header_sys_shm_h" = "yes"; then
AC_DEFINE(USE_SHM_COPROCESSING)
fi
dnl check whether we have a random device
if test "$try_dev_random" = yes ; then
AC_CACHE_CHECK(for random device, ac_cv_have_dev_random,
[if test -c "$NAME_OF_DEV_RANDOM" && test -c "$NAME_OF_DEV_URANDOM" ; then
ac_cv_have_dev_random=yes; else ac_cv_have_dev_random=no; fi])
if test "$ac_cv_have_dev_random" = yes; then
AC_DEFINE(HAVE_DEV_RANDOM)
fi
else
AC_MSG_CHECKING(for random device)
ac_cv_have_dev_random=no
AC_MSG_RESULT(has been disabled)
fi
dnl setup assembler stuff
AC_MSG_CHECKING(for mpi assembler functions)
if test -f $srcdir/mpi/config.links ; then
. $srcdir/mpi/config.links
WK_LINK_FILES($mpi_ln_src, $mpi_ln_dst)
ac_cv_mpi_extra_asm_modules="$mpi_extra_modules"
ac_cv_mpi_config_done="yes"
AC_MSG_RESULT(done)
else
AC_MSG_RESULT(failed)
AC_MSG_ERROR([mpi/config.links missing!])
fi
MPI_EXTRA_ASM_OBJS=""
if test "$ac_cv_mpi_extra_asm_modules" != ""; then
WK_MSG_PRINT([mpi extra asm functions:])
for i in $ac_cv_mpi_extra_asm_modules; do
WK_MSG_PRINT([$i])
MPI_EXTRA_ASM_OBJS="$MPI_EXTRA_ASM_OBJS $i.o"
done
AC_MSG_RESULT()
fi
AC_SUBST(MPI_EXTRA_ASM_OBJS)
dnl Do we have zlib? Must do it here because Solaris failed
dnl when compiling a conftest (due to the "-lz" from LIBS).
if test "$g10_force_zlib" = "yes"; then
ZLIBS="../zlib/libzlib.a"
AM_CONDITIONAL(ENABLE_LOCAL_ZLIB, true)
WK_LINK_FILES(zlib/zlib.h, zlib.h )
WK_LINK_FILES(zlib/zconf.h, zconf.h )
else
AC_CHECK_HEADERS(zlib.h)
if test "$ac_cv_header_zlib_h" = yes ; then
LIBS="$LIBS -lz"
ZLIBS=
AM_CONDITIONAL(ENABLE_LOCAL_ZLIB, false)
else
ZLIBS="../zlib/libzlib.a"
AM_CONDITIONAL(ENABLE_LOCAL_ZLIB, true)
WK_LINK_FILES(zlib/zlib.h, zlib.h )
WK_LINK_FILES(zlib/zconf.h, zconf.h )
fi
fi
AC_SUBST(ZLIBS)
WK_DO_LINK_FILES
AC_OUTPUT([
Makefile
intl/Makefile
po/Makefile.in
util/Makefile
mpi/Makefile
cipher/Makefile
g10/Makefile
doc/Makefile
tools/Makefile
zlib/Makefile
checks/Makefile
])
diff --git a/doc/gpg.1pod b/doc/gpg.1pod
index 2b6724088..56d802b08 100644
--- a/doc/gpg.1pod
+++ b/doc/gpg.1pod
@@ -1,491 +1,495 @@
=head1 NAME
gpg - GNU Privacy Guard
=head1 SYNOPSIS
B<gpg> [--homedir name] [--options file] [options] command [args]
B<gpgm> [--homedir name] [--options file] [options] command [args]
=head1 DESCRIPTION
B<gpg> is the main program for the GNUPG system. B<gpgm> is a maintenance
tool which has some commands B<gpgm> does not have; it is there because
it does not handle sensitive data and therefore has no need to allocate
secure memory.
=head1 COMMANDS
B<gpg> recognizes these commands:
B<-s>, B<--sign>
Make a signature. This option may be combined
with B<--encrypt>.
B<--clearsign>
Make a clear text signature.
B<-b>, B<--detach-sign>
Make a detached signature.
B<-e>, B<--encrypt>
Encrypt data. This option may be combined with B<--sign>.
B<-c>, B<--symmetric>
Encrypt with symmetric cipher only
This command asks for a passphrase.
B<--store>
store only (make a simple RFC1991 packet).
B<--decrypt> [I<file>]
Decrypt file (or stdin if no file is specified) and
write it to stdout (or the file specified with
B<--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.
B<--verify> [[I<sigfile>] {I<signed-files>}]
Assume that I<filename> is a signature and verify it
without generating any output. With no arguments,
the signature packet is read from stdin (it may be a
detached signature when not used in batch mode). If
only a sigfile is given, it may be a complete
signature or a detached signature, in which case
the signed stuff is expected in a file without the
I<.sig> or I<.asc> extension (if such a file does
not exist it is expected at stdin - use B<-> as
filename to force a read from stdin). With more than
1 argument, the first should be a detached signature
and the remaining files are the signed stuff.
B<-k> [I<username>] [I<keyring>]
Kludge to be somewhat compatible with PGP.
Without arguments, all public keyrings are listed.
With one argument, only I<keyring> is listed.
Special combinations are also allowed, but it may
give strange results when combined with more options.
B<-kv> Same as B<-k>
B<-kvv> List the signatures with every key.
B<-kvvv> Additionally check all signatures.
B<-kvc> List fingerprints
B<-kvvc> List fingerprints and signatures
-B<--list-keys> [I<names>]
+B<--list-keys> [I<names>]
List all keys from the public keyrings, or just the
ones given on the command line.
B<--list-secret-keys> [I<names>]
List all keys from the secret keyrings, or just the
ones given on the command line.
-B<--list-sigs> [I<names>]
+B<--list-sigs> [I<names>]
Same as B<--list-keys>, but the signatures are listed
too.
B<--check-sigs> [I<names>]
Same as B<--list-sigs>, but the signatures are verified.
B<--fingerprint> [I<names>]
List all keys with their fingerprints. This is the
same output as B<list-keys> but with the additonal output
of a line with the fingerprint. May also be combined
with B<--list-sigs> or B<--check-sigs>.
B<--list-packets>
List only the sequence of packets. This is mainly
useful for debugging.
B<--gen-key>
Generate a new key pair. This command can only be
used interactive.
B<--edit-key> I<name>
Present a menu which enables you to do all key
related tasks:
B<sign>
Make a signature on key of user I<name>.
If the key is not yet signed by the default
user (or the users given with B<-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 B<-u>.
B<trust>
Change the owner trust value. This updates the
trust-db immediately and no save is required.
B<adduid>
Create an alternate user id.
B<deluid>
Delete an user id.
B<addkey>
Add a subkey to this key.
B<delkey>
Remove a subkey.
B<expire>
- Change the key expiration time. If a key is
+ Change the key expiration time. If a key is
select, the time of this key will be changed.
With no selection the key expiration of the
primary key is changed.
B<passwd>
Change the passphrase of the secret key.
B<uid> I<n>
Toggle selection of user id with index I<n>.
Use 0 to deselect all.
B<key> I<n>
Toggle selection of subkey with index I<n>.
Use 0 to deselect all.
B<check>
Check all selected user ids.
B<pref>
List preferences.
B<toggle>
Toggle between public and secret key listing.
B<save>
Save all changes to the key rings and quit.
B<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
indicated by an asterisk. The trust value is
displayed with the primary key: The first one is the
assigned owner trust and the second the calculated
trust value; letters are used for the values:
B<-> No ownertrust assigned.
B<o> Trust not yet calculated.
B<e> Trust calculation failed.
B<q> Not enough information for calculation.
B<n> Never trust this key.
B<m> Marginally trusted.
B<f> Fully trusted.
B<u> Ultimately trusted
B<--delete-key>
Remove key from the public keyring
B<--delete-secret-key>
Remove key from the secret and public keyring
B<--gen-revoke>
Generate a revocation certificate.
B<--export> [I<names>]
Either export all keys from all keyrings (default
keyrings and those registered via option B<--keyring>),
or if at least one name is given, those of the given
name. The new keyring is written to F<stdout> or to
the file given with option "output". Use together
with B<-a> to mail those keys.
B<--export-secret-keys> [I<names>
Same as B<--export>, but does export the secret keys.
This is normally not very useful.
B<--import>, B<--fast-import>
- Import/merge keys. The fast version does not build
+ Import/merge keys. The fast version does not build
the trustdb; this can be deon at anytime with the
command B<--update-trustdb>.
B<--export-ownertrust>
List the assigned ownertrust values in ascii format
for backup purposes [B<gpgm> only].
B<--import-ownertrust> [I<filename>]
Update the trustdb with the ownertrust values stored
in I<filename> (or stdin if not given); existing
values will be overwritten. [B<gpgm> only].
=head1 OPTIONS
Long options can be put in an options file (default F<~/.gnupg/options>);
do not write the 2 dashes, but simply the name of the option and any
-arguments if required. Lines with a hash as the first non-white-space
+arguments if required. 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.
B<gpg> recognizes these options:
B<-a>, B<--armor>
Create ASCII armored output.
B<-o> I<file>, B<--output> I<file>
Write output to I<file>.
B<-u> I<name>, B<--local-user> I<name>
Use I<name> 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.
B<--default-key> I<name>
Use I<name> as default user-id for signatures. If this
is not used the default user-id is the first user-id
from the secret keyring.
B<-r> I<name>, B<--remote-user> I<name>
Use I<name> as the user-id for encryption.
This option is silently ignored for the list commands,
so that it can be used in an options file.
B<-v>, B<--verbose>
Give more information during processing. If used
twice, the input data is listed in detail.
B<-q>, B<--quiet>
Be somewhat more quiet in some cases.
B<-z> I<n>
Set compress level to I<n>. A value of 0 for I<n>
disables compression. Default is to use the default
compression level of zlib (which is 6).
B<-t>, B<--textmode>
- Use canonical text mode. Used to make clear-text
- signatures.
+ Use canonical text mode. If B<-t> (but not
+ B<--textmode>) is used together with armoring
+ and signing, this enables clearsigned messages.
+ This kludge is needed for PGP compatibility;
+ normally you would use B<--sign> or b<--clearsign>
+ to selected the type os signatures.
B<-n>, B<--dry-run>
Don't make any changes (not yet implemented).
B<--batch>
Batch mode; never ask, do not allow interactive
commands.
B<--no-batch>
Disable batch mode; this may be used if B<batch>
is used in the options file.
B<--yes>
Assume "yes" on most questions.
B<--no>
Assume "no" on most questions.
B<--keyring> I<file>
Add I<file> to the list of keyrings.
If I<file> 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 (F<~/.gnupg> if B<--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.
B<--secret-keyring> I<file>
Same as B<--keyring> but for secret keyrings.
B<--homedir> I<dir>
Set the name of the home directory to I<dir>. If this
option is not used it defaults to F<~/.gnupg>. It does
not make sense to use this in a options file. This
also overrides the environment variable C<GNUPGHOME>.
B<--options> I<file>
Read options from I<file> and do not try to read
them from the default options file in the homedir
(see B<--homedir>). This option is ignored when used
in an options file.
B<--no-options>
Shortcut for B<--options> I</dev/null>. This option is
detected before an attempt to open an option file.
B<--load-extension> I<modulename>
Load an extension module. If I<modulename> does not
contain a slash it is searched in B</usr/local/lib/gnupg>
See the manual for more information about extensions.
B<--debug> I<flags>
Set debugging flags. All flags are or-ed and I<flags> may
be given in C syntax (e.g. 0x0042).
B<--debug-all>
Set all useful debugging flags.
B<--status-fd> I<n>
Write special status strings to the file descriptor I<n>.
B<--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.
B<--comment> I<string>
Use I<string> as comment string in clear text signatures.
B<--set-filename> I<string>
Use I<string> as the name of file which is stored in
messages.
B<--completes-needed> I<n>
Number of completely trusted users to introduce a new
key signator (defaults to 1).
B<--marginals-needed> I<n>
Number of marginally trusted users to introduce a new
key signator (defaults to 3)
B<--cipher-algo> I<name>
Use I<name> as cipher algorithm. Running the program
with the command B<--version> yields a list of supported
algorithms. If this is not used the cipher algorithm is
selected from the preferences stored with the key.
B<--digest-algo> I<name>
Use I<name> as message digest algorithm. Running the
program with the command B<--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.
B<--s2k-cipher-algo> I<name>
Use I<name> as the cipher algorithm used to protect secret
keys. The default cipher is BLOWFISH. This cipher is
also used for conventional encryption if B<--cipher-algo>
is not given.
B<--s2k-digest-algo> I<name>
Use I<name> 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 B<--digest-algo> is not given.
B<--s2k-mode> I<number>
Selects how passphrases are mangled: A number of I<0>
uses the plain passphrase (which is not recommended),
a I<1> (default) adds a salt to the passphrase and
I<3> interates the whole process a couple of times.
Unless -B<--rfc1991> is used, this mode is also used
for conventional encryption.
B<--compress-algo> I<number>
Use compress algorithm I<number>. Default is I<2> which is
RFC1950 compression; you may use I<1> to use the old zlib
version which is used by PGP. This is only used for
new messages. The default algorithm may give better
results because the window size is not limited to 8K.
If this is not used the OpenPGP behaviour is used; i.e.
the compression algorith is selected from the preferences.
B<--digest-algo> I<name>
Use I<name> as message digest algorithm. Running the
program with the command B<--version> yields a list of
supported algorithms.
B<--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.
B<--passphrase-fd> I<n>
Read the passphrase from file descriptor I<n>. If you use
0 for I<n>, the passphrase will be read from stdin. This
can only be used if only one passphrase is supplied.
B<Don't use this option if you can avoid it>
B<--rfc1991>
Try to be more RFC1991 (PGP 2.x) compliant.
B<--force-v3-sigs>
OpenPGP states that a implemenation should generate
v4 signatures but PGP 5.x does only recognize such
signatures on key material. This options forces
v3 signatures for signatures on data.
B<--no-verbose>
Reset verbose level to 0.
B<--no-greeting>
Suppress the initial copyright message but do not
enter batch mode.
B<--no-armor>
Assume the input data is not in ASCCI armored format.
B<--no-default-keyring>
Do not add the default keyrings to the list of
keyrings.
B<--skip-verify>
Skip the signature verification step. This may be
used to make the encryption faster if the signature
verification is not needed.
B<--version>
Print version information along with a list
of supported algorithms.
B<--with-colons>
Print key listings delimited by colons.
B<--warranty>
Print warranty information.
B<-h>, B<--help>
Print usage information.
=head1 RETURN VALUE
The Program returns 0 if everything was fine, 1 if at least
a signature was bad and other errorcode for fatal errors.
=head1 EXAMPLES
- -se -r Bob [file] sign and encrypt for user Bob
- -sat [file] make a clear text signature
- -sb [file] make a detached signature
- -k [userid] show keys
- -kc [userid] show fingerprint
+ -se -r Bob [file] sign and encrypt for user Bob
+ -sat [file] make a clear text signature
+ -sb [file] make a detached signature
+ -k [userid] show keys
+ -kc [userid] show fingerprint
=head1 ENVIRONMENT
C<HOME> Used to locate the default home directory.
C<GNUPGHOME> If set, direcory used instead of F<~/.gnupg>.
=head1 FILES
F<~/.gnupg/secring.gpg> The secret keyring
F<~/.gnupg/pubring.gpg> The public keyring
F<~/.gnupg/trustdb.gpg> The trust database
-F<~/.gnupg/options> May contain options
+F<~/.gnupg/options> May contain options
F</usr[/local]/lib/gnupg/> Default location for extensions
=head1 SEE ALSO
-gpg(1) gpgm(1)
+gpg(1) gpgm(1)
=head1 WARNINGS
Use a B<good> password for your user account and a B<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 B<~/.gnupg/>
directory very good.
Keep in mind that, if this program is used over a network (telnet), it
is B<very> easy to spy out your passphrase!
=head1 BUGS
On many systems this program should be installed as setuid(root); this
is necessary to lock some pages of memory. If you get no warning message
about insecure memory your OS kernel supports locking without being root;
setuid is dropped as soon as this memory is allocated.
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 35afefa16..9109e6558 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,1179 +1,1206 @@
+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 represenation 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 <bem@cmc.net>
* 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 <mroth@nessie.de>
* 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/export.c b/g10/export.c
index 46413fff2..637f675f4 100644
--- a/g10/export.c
+++ b/g10/export.c
@@ -1,156 +1,165 @@
/* export.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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "options.h"
#include "packet.h"
#include "errors.h"
#include "keydb.h"
#include "memory.h"
#include "util.h"
#include "main.h"
static int do_export( STRLIST users, int secret );
/****************
* Export the public keys (to standard out or --output).
* Depending on opt.armor the output is armored.
* If USERS is NULL, the complete ring will be exported.
*/
int
export_pubkeys( STRLIST users )
{
return do_export( users, 0 );
}
int
export_seckeys( STRLIST users )
{
return do_export( users, 1 );
}
static int
do_export( STRLIST users, int secret )
{
int rc = 0;
armor_filter_context_t afx;
compress_filter_context_t zfx;
IOBUF out = NULL;
PACKET pkt;
KBNODE keyblock = NULL;
KBNODE kbctx, node;
KBPOS kbpos;
STRLIST sl;
int all = !users;
int any=0;
memset( &afx, 0, sizeof afx);
memset( &zfx, 0, sizeof zfx);
init_packet( &pkt );
if( (rc = open_outfile( NULL, 0, &out )) )
goto leave;
if( opt.armor ) {
afx.what = secret?5:1;
iobuf_push_filter( out, armor_filter, &afx );
}
if( opt.compress_keys && opt.compress )
iobuf_push_filter( out, compress_filter, &zfx );
if( all ) {
rc = enum_keyblocks( secret?5:0, &kbpos, &keyblock );
if( rc ) {
if( rc != -1 )
log_error("enum_keyblocks(open) failed: %s\n", g10_errstr(rc) );
goto leave;
}
all = 2;
}
/* use the correct sequence. strlist_last,prev do work correctly with
* NULL pointers :-) */
for( sl=strlist_last(users); sl || all ; sl=strlist_prev( users, sl )) {
if( all ) { /* get the next user */
rc = enum_keyblocks( 1, &kbpos, &keyblock );
if( rc == -1 ) /* EOF */
break;
if( rc ) {
log_error("enum_keyblocks(read) failed: %s\n", g10_errstr(rc));
break;
}
}
else {
/* search the userid */
rc = secret? find_secret_keyblock_byname( &kbpos, sl->d )
: find_keyblock_byname( &kbpos, sl->d );
if( rc ) {
log_error("%s: user not found: %s\n", sl->d, g10_errstr(rc) );
rc = 0;
continue;
}
/* read the keyblock */
rc = read_keyblock( &kbpos, &keyblock );
}
if( rc ) {
log_error("certificate read problem: %s\n", g10_errstr(rc));
goto leave;
}
/* and write it */
for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) {
/* don't export any comment packets but those in the
* secret keyring */
if( !secret && node->pkt->pkttype == PKT_COMMENT )
continue;
+ /* do not export packets which are marked as not exportable */
+ if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ const char *p;
+ p = parse_sig_subpkt2( node->pkt->pkt.signature,
+ SIGSUBPKT_EXPORTABLE, NULL );
+ if( p && !*p )
+ continue; /* not exportable */
+ }
+
if( (rc = build_packet( out, node->pkt )) ) {
log_error("build_packet(%d) failed: %s\n",
node->pkt->pkttype, g10_errstr(rc) );
rc = G10ERR_WRITE_FILE;
goto leave;
}
}
any++;
}
if( rc == -1 )
rc = 0;
leave:
if( all == 2 )
enum_keyblocks( 2, &kbpos, &keyblock ); /* close */
release_kbnode( keyblock );
if( rc || !any )
iobuf_cancel(out);
else
iobuf_close(out);
if( !any )
log_info("warning: nothing exported\n");
return rc;
}
diff --git a/g10/g10.c b/g10/g10.c
index 3df9121b5..9d4f20d79 100644
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -1,1393 +1,1396 @@
/* g10.c - The GnuPG utility (main for gpg)
* 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 <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* #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 "mpi.h"
#include "cipher.h"
#include "filter.h"
#include "trustdb.h"
#include "ttyio.h"
#include "i18n.h"
#include "status.h"
#include "g10defs.h"
#ifndef IS_G10MAINT
#define IS_G10 1
#endif
enum cmd_and_opt_values { aNull = 0,
oArmor = 'a',
aDetachedSign = 'b',
aSym = 'c',
aDecrypt = 'd',
aEncr = 'e',
oKOption = 'k',
oDryRun = 'n',
oOutput = 'o',
oQuiet = 'q',
oRemote = 'r',
aSign = 's',
- oTextmode = 't',
+ oTextmodeShort= 't',
oUser = 'u',
oVerbose = 'v',
oCompress = 'z',
oBatch = 500,
aClearsign,
aStore,
aKeygen,
aSignEncr,
aSignKey,
aListPackets,
aEditKey,
aDeleteKey,
aDeleteSecretKey,
aKMode,
aKModeC,
aImport,
aFastImport,
aVerify,
aListKeys,
aListSigs,
aListSecretKeys,
aExport,
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,
oCompletesNeeded,
oMarginalsNeeded,
oLoadExtension,
oRFC1991,
oCipherAlgo,
oDigestAlgo,
oCompressAlgo,
oPasswdFD,
oQuickRandom,
oNoVerbose,
oTrustDBName,
oNoSecmemWarn,
oNoArmor,
oNoDefKeyring,
oNoGreeting,
oNoOptions,
oNoBatch,
oHomedir,
oWithColons,
oSkipVerify,
oCompressKeys,
oCompressSigs,
oAlwaysTrust,
oEmuChecksumBug,
oRunAsShmCP,
oSetFilename,
oComment,
oThrowKeyid,
oForceV3Sigs,
oS2KMode,
oS2KDigest,
oS2KCipher,
aTest };
static ARGPARSE_OPTS opts[] = {
{ 300, NULL, 0, N_("@Commands:\n ") },
#ifdef IS_G10
{ 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")},
#endif
{ aListKeys, "list-keys", 256, N_("list keys")},
{ 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")},
#ifdef IS_G10
{ aKeygen, "gen-key", 256, N_("generate a new key pair")},
#endif
{ aDeleteKey, "delete-key",256, N_("remove key from the public keyring")},
#ifdef IS_G10
{ aEditKey, "edit-key" ,256, N_("sign or edit a key")},
{ aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")},
#endif
{ aExport, "export" , 256, N_("export keys") },
{ 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")},
#ifdef IS_G10MAINT
{ 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
#endif
{ 301, NULL, 0, N_("@\nOptions:\n ") },
{ oArmor, "armor", 0, N_("create ascii armored output")},
#ifdef IS_G10
{ oUser, "local-user",2, N_("use this user-id to sign or decrypt")},
{ oRemote, "remote-user", 2, N_("use this user-id for encryption")},
{ oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") },
+ { oTextmodeShort, NULL, 0, "@"},
{ oTextmode, "textmode", 0, N_("use canonical text mode")},
#endif
{ oOutput, "output", 2, N_("use as output file")},
{ oVerbose, "verbose", 0, N_("verbose") },
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
{ oForceV3Sigs, "force-v3-sigs", 0, N_("force v3 signatures") },
/* { oDryRun, "dry-run", 0, N_("do not make any changes") }, */
{ 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")},
{ 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)")},
{ oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")},
{ oRFC1991, "rfc1991", 0, N_("emulate the mode described in RFC1991")},
{ 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")},
#ifdef IS_G10
{ 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")},
#else /* some dummies */
{ oCipherAlgo, "cipher-algo", 2 , "@"},
{ oDigestAlgo, "digest-algo", 2 , "@"},
{ oCompressAlgo, "compress-algo", 1 , "@"},
#endif
#ifdef IS_G10
{ 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" ) },
#endif
/* hidden options */
#ifdef IS_G10MAINT
{ aTest, "test" , 0, "@" },
{ aExportOwnerTrust, "list-ownertrust",0 , "@"}, /* alias */
{ aListTrustDB, "list-trustdb",0 , "@"},
{ aListTrustPath, "list-trust-path",0, "@"},
#endif
#ifdef IS_G10
{ oKOption, NULL, 0, "@"},
{ aEditKey, "edit-sig" ,0, "@"}, /* alias for edit-key */
{ oPasswdFD, "passphrase-fd",1, "@" },
{ aSignKey, "sign-key" ,256, "@" }, /* alias for edit-key */
#endif
{ 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, "@"},
{ 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, "@" },
{ oComment, "comment", 2, "@" },
{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 );
#ifdef IS_G10MAINT
static void print_hex( byte *p, size_t n );
static void print_mds( const char *fname, int algo );
#endif
const char *
strusage( int level )
{
static char *digests, *pubkeys, *ciphers;
const char *p;
switch( level ) {
case 11: p =
#ifdef IS_G10MAINT
"gpgm (GnuPG)";
#else
"gpg (GnuPG)";
#endif
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p =
_("Please report bugs to <gnupg-bugs@gnu.org>.\n");
break;
case 1:
case 40: p =
#ifdef IS_G10MAINT
_("Usage: gpgm [options] [files] (-h for help)");
#else
_("Usage: gpg [options] [files] (-h for help)");
#endif
break;
case 41: p =
#ifdef IS_G10MAINT
_("Syntax: gpgm [options] [files]\n"
"GnuPG maintenance utility\n");
#else
_("Syntax: gpg [options] [files]\n"
"sign, check, encrypt or decrypt\n"
"default operation depends on the input data\n");
#endif
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)
{
#ifdef IS_G10MAINT
fputs(_("usage: gpgm [options] "),stderr);
#else
fputs(_("usage: gpg [options] "),stderr);
#endif
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 errors=0;
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 */
#ifdef IS_G10MAINT
secmem_init( 0 ); /* disable use of secmem */
maybe_setuid = 0;
log_set_name("gpgm");
#else
/* 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();
#endif
init_signals();
i18n_init();
opt.compress = -1; /* defaults to standard compress level */
/* fixme: set the next two to zero and decide where used */
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.def_compress_algo = 2;
opt.s2k_mode = 1; /* salted */
opt.s2k_digest_algo = DIGEST_ALGO_RMD160;
opt.s2k_cipher_algo = CIPHER_ALGO_BLOWFISH;
opt.completes_needed = 1;
opt.marginals_needed = 3;
opt.homedir = getenv("GNUPGHOME");
if( !opt.homedir || !*opt.homedir ) {
#ifdef __MINGW32__
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;
}
#endif
}
#ifdef USE_SHM_COPROCESSING
if( opt.shm_coprocess ) {
#ifdef IS_G10
init_shm_coprocessing(requested_shm_size, 1 );
#else
init_shm_coprocessing(requested_shm_size, 0 );
#endif
}
#endif
#ifdef IS_G10
/* initialize the secure memory. */
secmem_init( 16384 );
maybe_setuid = 0;
/* Okay, we are now working under our real uid */
#endif
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 aExport: set_cmd( &cmd, aExport); 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;
#ifdef IS_G10
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;
#else
#ifdef MAINTAINER_OPTIONS
case aPrimegen: set_cmd( &cmd, aPrimegen); break;
case aTest: set_cmd( &cmd, aTest); 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); break;
case aEnArmor: set_cmd( &cmd, aEnArmor); break;
case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break;
case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break;
#endif /* IS_G10MAINT */
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 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 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 oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break;
case oMarginalsNeeded: opt.marginals_needed = 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 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; 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 oComment: opt.comment_string = pargs.r.ret_str; break;
case oThrowKeyid: opt.throw_keyid = 1; break;
case oForceV3Sigs: opt.force_v3_sigs = 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;
#ifdef IS_G10
case oRemote: /* store the remote users */
sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str));
strcpy(sl->d, pargs.r.ret_str);
sl->next = remusr;
remusr = sl;
break;
+ case oTextmodeShort: opt.textmode = 2; break;
case oTextmode: opt.textmode=1; break;
case oUser: /* store the local users */
sl = m_alloc( sizeof *sl + strlen(pargs.r.ret_str));
strcpy(sl->d, pargs.r.ret_str);
sl->next = locusr;
locusr = sl;
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;
#else
case oCipherAlgo:
case oDigestAlgo:
case oNoSecmemWarn:
break; /* dummies */
#endif
default : errors++; 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 ) {
tty_printf("%s %s; %s\n", strusage(11), strusage(13), strusage(14) );
tty_printf("%s\n", strusage(15) );
}
secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */
set_debug();
/* 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.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"));
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 && !detached_sig && opt.armor && cmd == aSign )
+ 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 = init_trustdb(1, trustdb_name );
break;
case aExportOwnerTrust: rc = init_trustdb( 0, trustdb_name ); break;
case aListTrustDB: rc = init_trustdb( argc? 1:0, trustdb_name ); break;
default: rc = init_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;
#ifdef IS_G10
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 != 1 )
wrong_args(_("--edit-key username"));
keyedit_menu(fname, locusr );
break;
#endif /* IS_G10 */
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! */
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;
#ifdef IS_G10
case aKeygen: /* generate a key (interactive) */
if( argc )
wrong_args("--gen-key");
generate_keypair();
break;
#endif
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:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist( &sl, *argv );
export_pubkeys( sl );
free_strlist(sl);
break;
case aExportSecret:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist( &sl, *argv );
export_seckeys( sl );
free_strlist(sl);
break;
#ifdef IS_G10
case aGenRevoke:
if( argc != 1 )
wrong_args("--gen-revoke user-id");
gen_revoke( *argv );
break;
#endif
#ifdef IS_G10MAINT
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 level = atoi(*argv);
for(;;) {
byte *p = get_random_bits( 8, level, 0);
if( argc == 1 ) {
printf("%02x", *p );
fflush(stdout);
}
else
putchar(c&0xff);
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;
#ifdef MAINTAINER_OPTIONS
case aTest: do_test( argc? atoi(*argv): 1 ); break;
#endif /* MAINTAINER OPTIONS */
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 != 2 )
wrong_args("--list-trust-path [-- -]<maxdepth> <username>");
list_trust_path( atoi(*argv), argv[1] );
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;
#endif /* IS_G10MAINT */
case aListPackets:
opt.list_packets=1;
default:
/* fixme: g10maint should do regular maintenace tasks here */
if( argc > 1 )
wrong_args(_("[filename]"));
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;
}
proc_packets( a );
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:0;
/*write_status( STATUS_LEAVE );*/
exit(rc );
}
void
do_not_use_RSA()
{
static int did_rsa_note = 0;
if( !did_rsa_note ) {
did_rsa_note = 1;
log_info(_("RSA keys are deprecated; please consider "
"creating a new key and use this key in the future\n"));
}
}
#ifdef IS_G10MAINT
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);
}
#ifdef MAINTAINER_OPTIONS
static void
do_test(int times)
{
m_check(NULL);
}
#endif /* MAINTAINER OPTIONS */
#endif /* IS_G10MAINT */
diff --git a/g10/import.c b/g10/import.c
index 075ecd358..0cd1353ef 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1,943 +1,943 @@
/* import.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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#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 read_block( IOBUF a, compress_filter_context_t *cfx,
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 merge_sigs( 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 )
{
armor_filter_context_t afx;
compress_filter_context_t cfx;
PACKET *pending_pkt = NULL;
IOBUF inp = NULL;
KBNODE keyblock;
int rc = 0;
ulong count=0;
memset( &afx, 0, sizeof afx);
memset( &cfx, 0, sizeof cfx);
afx.only_keyblocks = 1;
/* fixme: don't use static variables */
memset( &stats, 0, sizeof( stats ) );
/* open file */
inp = iobuf_open(fname);
if( !fname )
fname = "[stdin]";
if( !inp ) {
log_error_f(fname, _("can't open file: %s\n"), strerror(errno) );
return G10ERR_OPEN_FILE;
}
if( !opt.no_armor ) /* armored reading is not disabled */
iobuf_push_filter( inp, armor_filter, &afx );
while( !(rc = read_block( inp, &cfx, &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_f(fname, _("skipping block of type %d\n"),
keyblock->pkt->pkttype );
}
release_kbnode(keyblock);
if( rc )
break;
if( !(++count % 100) )
log_info(_("%lu keys so far processed\n"), count );
}
if( rc == -1 )
rc = 0;
else if( rc && rc != G10ERR_INV_KEYRING )
log_error_f( fname, _("read error: %s\n"), g10_errstr(rc));
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 );
iobuf_close(inp);
return rc;
}
/****************
* Read the next keyblock from stream A, CFX is used to handle
* compressed keyblocks. 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, compress_filter_context_t *cfx,
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;
}
cfx->algo = pkt->pkt.compressed->algorithm;
pkt->pkt.compressed->buf = NULL;
iobuf_push_filter( a, compress_filter, cfx );
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_f( fname, "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_f(fname, _("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_f( fname, _("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_f( fname, _("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_f( fname, _("writing to '%s'\n"),
keyblock_resource_name(&kbpos) );
if( (rc=lock_keyblock( &kbpos )) )
log_error_f( keyblock_resource_name(&kbpos),
_("can't lock public keyring: %s\n"), g10_errstr(rc) );
else if( (rc=insert_keyblock( &kbpos, keyblock )) )
log_error_f( keyblock_resource_name(&kbpos),
_("can't write to keyring: %s\n"), g10_errstr(rc) );
unlock_keyblock( &kbpos );
/* we are ready */
if( !opt.quiet )
log_info_f( fname, _("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_f( fname, _("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_f(fname,
_("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_f(fname,
_("key %08lX: can't read original keyblock: %s\n"),
(ulong)keyid[1], g10_errstr(rc));
goto leave;
}
/* 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( opt.verbose > 1 )
log_info_f(keyblock_resource_name(&kbpos),
_("writing keyblock\n"));
if( (rc=lock_keyblock( &kbpos )) )
log_error_f(keyblock_resource_name(&kbpos),
_("can't lock public keyring: %s\n"), g10_errstr(rc) );
else if( (rc=update_keyblock( &kbpos, keyblock_orig )) )
log_error_f( keyblock_resource_name(&kbpos),
_("can't write keyblock: %s\n"), g10_errstr(rc) );
unlock_keyblock( &kbpos );
/* we are ready */
if( !opt.quiet ) {
if( n_uids == 1 )
log_info_f(fname, _("key %08lX: 1 new user-id\n"),
(ulong)keyid[1]);
else if( n_uids )
log_info_f(fname, _("key %08lX: %d new user-ids\n"),
(ulong)keyid[1], n_uids );
if( n_sigs == 1 )
log_info_f(fname, _("key %08lX: 1 new signature\n"),
(ulong)keyid[1]);
else if( n_sigs )
log_info_f(fname, _("key %08lX: %d new signatures\n"),
(ulong)keyid[1], n_sigs );
if( n_subk == 1 )
log_info_f(fname, _("key %08lX: 1 new subkey\n"),
(ulong)keyid[1]);
else if( n_subk )
log_info_f(fname, _("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_f(fname, _("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 );
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, NULL );
+ 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_f(fname, "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_f(fname, _("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( opt.verbose > 1 )
log_info_f(keyblock_resource_name(&kbpos), _("writing keyblock\n"));
if( (rc=lock_keyblock( &kbpos )) )
log_error_f( keyblock_resource_name(&kbpos),
_("can't lock secret keyring: %s\n"), g10_errstr(rc) );
else if( (rc=insert_keyblock( &kbpos, keyblock )) )
log_error_f(keyblock_resource_name(&kbpos),
_("can't write keyring: %s\n"), g10_errstr(rc) );
unlock_keyblock( &kbpos );
/* we are ready */
log_info_f(fname, _("key %08lX: secret key imported\n"), (ulong)keyid[1]);
stats.secret_imported++;
}
else if( !rc ) { /* we can't merge secret keys */
log_error_f(fname, _("key %08lX: already in secret keyring\n"),
(ulong)keyid[1]);
stats.secret_dups++;
}
else
log_error_f(fname, _("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_f(fname, _("key %08lX: no public key - "
"can't apply revocation certificate\n"), (ulong)keyid[1]);
rc = 0;
goto leave;
}
else if( rc ) {
log_error_f(fname, _("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_f(fname,
_("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_f(fname,
_("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_f(fname, _("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( opt.verbose > 1 )
log_info_f( keyblock_resource_name(&kbpos), _("writing keyblock\n"));
if( (rc=lock_keyblock( &kbpos )) )
log_error_f( keyblock_resource_name(&kbpos),
_("can't lock public keyring: %s\n"), g10_errstr(rc) );
else if( (rc=update_keyblock( &kbpos, keyblock )) )
log_error_f(keyblock_resource_name(&kbpos),
_("can't write keyblock: %s\n"), g10_errstr(rc) );
unlock_keyblock( &kbpos );
/* we are ready */
if( !opt.quiet )
log_info_f(fname, _("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.
*/
static int
chk_self_sigs( const char *fname, KBNODE keyblock,
PKT_public_key *pk, u32 *keyid )
{
KBNODE n, unode;
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] ) {
unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
if( !unode ) {
log_error_f(fname, _("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_f( fname, 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 user-id 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;
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_f(fname, _("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 ){
delete_kbnode( node->next );
node = node->next;
}
}
else
nvalid++;
}
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
&& node->pkt->pkt.signature->sig_class == 0x20 ) {
if( uid_seen ) {
log_error_f(fname, _("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_f(fname, _("key %08lX: invalid revocation "
"certificate: %s - skipped\n"),
(ulong)keyid[1], g10_errstr(rc));
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;
}
/****************
* 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.
* FIXME: add the check that we don't have duplicate signatures and the
* warning in cases where the old/new signatures don't match.
* 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;
node->flag |= 1;
log_info_f(fname, _("key %08lX: revocation certificate added\n"),
(ulong)keyid[1]);
}
}
}
/* 2nd: try to merge new ones 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->flag & 1)
&& 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->flag & 1) && 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->flag & 1)
&& onode->pkt->pkttype == PKT_USER_ID
&& cmp_user_ids( onode->pkt->pkt.user_id,
node->pkt->pkt.user_id ) )
break;
if( !node ) { /* this is a new user id: append */
rc = append_uid( keyblock_orig, node, n_sigs, fname, keyid);
if( rc )
return rc;
++*n_uids;
}
}
}
/* 4th: add new subkeys */
/* FIXME */
return 0;
}
/****************
* append the userid starting with NODE and all signatures to KEYBLOCK.
* Mark all new and copied packets by setting flag bit 0.
*/
static int
append_uid( KBNODE keyblock, KBNODE node, int *n_sigs,
const char *fname, u32 *keyid )
{
KBNODE n;
assert(node->pkt->pkttype == PKT_USER_ID );
/* at lease a self signature comes next to the user-id */
if( node->next->pkt->pkttype == PKT_USER_ID ) {
log_error_f(fname, _("key %08lX: our copy has no self-signature\n"),
(ulong)keyid[1]);
return G10ERR_GENERAL;
}
for( ;node && node->pkt->pkttype != PKT_USER_ID; node = node->next ) {
/* we add a clone to the original keyblock, because this
* one is released first */
n = clone_kbnode(node);
add_kbnode( keyblock, n );
node->flag |= 1;
n->flag |= 1;
if( n->pkt->pkttype == PKT_SIGNATURE )
++*n_sigs;
}
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_f(fname, _("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] ) {
found++;
break;
}
if( found ) { /* we already have this signature */
/* Hmmm: should we compare the timestamp etc?
* but then we have first to see whether this signature is valid
* - or simply add it in such a case and let trustdb logic
* decide whether to remove the old one
*/
continue;
}
/* This signature is new, append N to DST it.
* 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;
}
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 62921349c..a2b0a761f 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -1,1422 +1,1422 @@
/* keyedit.c - keyedit stuff
* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#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 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 index );
static int menu_select_key( KBNODE keyblock, int index );
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 );
#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 */
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;
}
/****************
* 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 rc;
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 ) {
PKT_signature *sig = node->pkt->pkt.signature;
int sigrc, selfsig;
switch( (rc = check_key_signature( keyblock, node, &selfsig)) ) {
case 0:
node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR);
sigrc = '!';
break;
case G10ERR_BAD_SIGN:
node->flag = NODFLG_BADSIG;
sigrc = '-';
inv_sigs++;
break;
case G10ERR_NO_PUBKEY:
node->flag = NODFLG_NOKEY;
sigrc = '?';
no_key++;
break;
default:
node->flag = NODFLG_SIGERR;
sigrc = '%';
oth_err++;
break;
}
if( sigrc != '?' ) {
tty_printf("sig%c %08lX %s ",
sigrc, sig->keyid[1], datestr_from_sig(sig));
if( sigrc == '%' )
tty_printf("[%s] ", g10_errstr(rc) );
else if( sigrc == '?' )
;
else if( selfsig ) {
tty_printf( _("[self-signature]") );
if( sigrc == '!' )
has_selfsig = 1;
}
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");
/* fixme: 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;
}
/****************
* 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.
*
* fixme: Add support for our proposed sign-all scheme
*/
static int
sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified )
{
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;
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] ) {
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( !cpr_get_answer_is_yes(N_("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;
assert( primary_pk );
node->flag &= ~NODFLG_MARK_A;
rc = make_keysig_packet( &sig, primary_pk,
node->pkt->pkt.user_id,
NULL,
sk,
0x10, 0, NULL, NULL );
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 );
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(N_("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 fixed.
*/
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 )
{
enum cmdids { cmdNONE = 0,
cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
cmdDEBUG, cmdSAVE, cmdADDUID, cmdDELUID, cmdADDKEY, cmdDELKEY,
cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
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_("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_("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") },
{ 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;
if( opt.batch ) {
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( 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);
answer = cpr_get(N_("keyedit.cmd"), _("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 to 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( !modified && !sec_modified )
goto leave;
if( !cpr_get_answer_is_yes(N_("keyedit.save.okay"),
_("Save changes? ")) ) {
if( cpr_enabled()
|| cpr_get_answer_is_yes(N_("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, NULL );
+ rc = update_trust_record( keyblock, 0, NULL );
if( rc )
log_error(_("update of trust db 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) */
if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) ) {
if( !cpr_get_answer_is_yes(N_("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 );
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, NULL );
+ rc = update_trust_record( keyblock, 0, NULL );
if( rc ) {
log_error(_("update of trust db 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(
N_("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 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(
N_("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 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;
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;
/* 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);
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( 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) );
}
}
/* 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, sk );
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 );
}
/****************
* 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, 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 exiration time for a secondary key.\n"));
else {
tty_printf(_("Changing exiration time for the primary key.\n"));
mainkey=1;
}
expiredate = ask_expiredate();
/* fixme: check that expiredate is > key creation date */
/* get the secret key , make a copy and set the expiration time into
* that key (because keygen_add-key-expire expects it there)
*/
node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
sk->expiredate = expiredate;
/* Now we can actually change the self signature(s) */
main_pk = sub_pk = NULL;
uid = NULL;
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 );
}
else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
&& (node->flag & NODFLG_SELKEY ) )
sub_pk = node->pkt->pkt.public_key;
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 should be replaced */
PKT_signature *newsig;
PACKET *newpkt;
KBNODE sn;
/* find the corresponding secret self-signature */
for( sn=sec_keyblock; sn; sn = sn->next ) {
if( sn->pkt->pkttype == PKT_SIGNATURE
&& !cmp_signatures( sn->pkt->pkt.signature, sig ) )
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, sk );
else
rc = make_keysig_packet( &newsig, main_pk, NULL, sub_pk,
sk, 0x18, 0,
keygen_add_key_expire, sk );
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 index )
{
KBNODE node;
int i;
/* first check that the index is valid */
if( index ) {
for( i=0, node = keyblock; node; node = node->next ) {
if( node->pkt->pkttype == PKT_USER_ID ) {
if( ++i == index )
break;
}
}
if( !node ) {
tty_printf(_("No user id with index %d\n"), index );
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 == index )
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 index )
{
KBNODE node;
int i;
/* first check that the index is valid */
if( 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 == index )
break;
}
}
if( !node ) {
tty_printf(_("No secondary key with index %d\n"), index );
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 == index )
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);
}
diff --git a/g10/keygen.c b/g10/keygen.c
index 6b04971ca..a17d47927 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -1,1019 +1,1024 @@
/* keygen.c - generate a key pair
* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#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_secret_key *sk = opaque;
byte buf[8];
u32 u;
if( sk->expiredate ) {
u = sk->expiredate;
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_BLOWFISH;
buf[1] = CIPHER_ALGO_CAST5;
build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 2 );
buf[0] = DIGEST_ALGO_RMD160;
buf[1] = DIGEST_ALGO_SHA1;
buf[2] = DIGEST_ALGO_TIGER;
buf[3] = DIGEST_ALGO_MD5;
build_sig_subpkt( sig, SIGSUBPKT_PREF_HASH, buf, 4 );
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, sk );
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, sk );
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 expiredate,
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( expiredate && expiredate < sk->timestamp )
expiredate = sk->timestamp; /* key generatio may take long */
sk->expiredate = pk->expiredate = expiredate;
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 expiredate )
{
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( expiredate && expiredate < pk->timestamp )
expiredate = pk->timestamp; /* key generation may take long */
sk->expiredate = pk->expiredate = expiredate;
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) ElGamal (sign and encrypt)\n"), 2 );
if( addmode )
tty_printf( _(" (%d) ElGamal (encrypt only)\n"), 3 );
tty_printf( _(" (%d) DSA (sign only)\n"), 4 );
tty_printf( _(" (%d) ElGamal in a v3 packet\n"), 5 );
*ret_v4 = 1;
for(;;) {
answer = cpr_get(N_("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 == 2 ) {
algo = PUBKEY_ALGO_ELGAMAL;
break;
}
else if( algo == 3 && addmode ) {
algo = PUBKEY_ALGO_ELGAMAL_E;
break;
}
else if( algo == 4 ) {
algo = PUBKEY_ALGO_DSA;
break;
}
else if( algo == 5 ) {
algo = PUBKEY_ALGO_ELGAMAL_E;
*ret_v4 = 0;
break;
}
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(N_("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 > 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(N_("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(N_("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;
}
u32
ask_expiredate()
{
char *answer;
int valid_days=0;
u32 expiredate = 0;
tty_printf(_("Please specify how long the key should be valid.\n"
" 0 = key does not expire\n"
" <n> = key expires in n days\n"
" <n>w = key expires in n weeks\n"
" <n>m = key expires in n months\n"
" <n>y = key expires in n years\n"));
/* Note: The elgamal subkey for DSA has no exiration 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(N_("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"));
expiredate = 0;
}
else {
expiredate = make_timestamp() + valid_days * 86400L;
/* print the date when the key expires */
tty_printf(_("Key expires at %s\n"), asctimestamp(expiredate) );
}
if( !cpr_enabled()
&& cpr_get_answer_is_yes(N_("keygen.valid.okay"),
_("Is this correct (y/n)? ")) )
break;
}
m_free(answer);
return expiredate;
}
static int
has_invalid_email_chars( const char *s )
{
int at_seen=0;
for( ; *s; s++ ) {
if( *s & 0x80 )
return 1;
if( *s == '@' )
at_seen=1;
else if( !at_seen
&& !strchr("01234567890abcdefghijklmnopqrstuvwxyz_-.+", *s ))
return 1;
else if( at_seen
&& !strchr("01234567890abcdefghijklmnopqrstuvwxyz_-.", *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) <heinrichh@duesseldorf.de>\"\n\n") );
uid = aname = acomment = amail = NULL;
for(;;) {
char *p;
if( !aname ) {
for(;;) {
m_free(aname);
aname = cpr_get(N_("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(N_("keygen.email"),_("Email address: "));
trim_spaces(amail);
strlwr(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(N_("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!)" );
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(N_("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 );
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 (work in another window, move the mouse, utilize the\n"
"network and the disks) during the prime generation; this gives the random\n"
"number 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 cancelled
*/
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 expiredate;
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 );
expiredate = ask_expiredate();
uid = ask_user_id(0);
if( !uid ) {
log_error(_("Key generation cancelled.\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, expiredate, 1);
else
rc = do_create( algo, nbits, pub_root, sec_root,
dek, s2k, &sk, expiredate, 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, expiredate, 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 \"--add-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 expiredate;
unsigned nbits;
char *passphrase = NULL;
DEK *dek = NULL;
STRING2KEY *s2k = NULL;
/* 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 );
/* 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 );
expiredate = ask_expiredate();
if( !cpr_enabled() && !cpr_get_answer_is_yes(N_("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 );
}
rc = do_create( algo, nbits, pub_keyblock, sec_keyblock,
dek, s2k, NULL, expiredate, 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/parse-packet.c b/g10/parse-packet.c
index 542676ccd..1f19b05de 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -1,1412 +1,1417 @@
/* parse-packet.c - read packets
* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "packet.h"
#include "iobuf.h"
#include "mpi.h"
#include "util.h"
#include "cipher.h"
#include "memory.h"
#include "filter.h"
#include "options.h"
#include "main.h"
static int mpi_print_mode = 0;
static int list_mode = 0;
static int parse( IOBUF inp, PACKET *pkt, int reqtype,
ulong *retpos, int *skip, IOBUF out, int do_skip );
static int copy_packet( IOBUF inp, IOBUF out, int pkttype,
unsigned long pktlen );
static void skip_packet( IOBUF inp, int pkttype, unsigned long pktlen );
static void skip_rest( IOBUF inp, unsigned long pktlen );
static void *read_rest( IOBUF inp, ulong *r_pktlen );
static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_signature *sig );
static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_onepass_sig *ops );
static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
byte *hdr, int hdrlen, PACKET *packet );
static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen );
static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb);
static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb );
static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb);
static unsigned short
read_16(IOBUF inp)
{
unsigned short a;
a = iobuf_get_noeof(inp) << 8;
a |= iobuf_get_noeof(inp);
return a;
}
static unsigned long
read_32(IOBUF inp)
{
unsigned long a;
a = iobuf_get_noeof(inp) << 24;
a |= iobuf_get_noeof(inp) << 16;
a |= iobuf_get_noeof(inp) << 8;
a |= iobuf_get_noeof(inp);
return a;
}
int
set_packet_list_mode( int mode )
{
int old = list_mode;
list_mode = mode;
mpi_print_mode = DBG_MPI;
return old;
}
static void
unknown_pubkey_warning( int algo )
{
static byte unknown_pubkey_algos[256];
algo &= 0xff;
if( !unknown_pubkey_algos[algo] ) {
if( opt.verbose )
log_info("can't handle public key algorithm %d\n", algo );
unknown_pubkey_algos[algo] = 1;
}
}
/****************
* Parse a Packet and return it in packet
* Returns: 0 := valid packet in pkt
* -1 := no more packets
* >0 := error
* Note: The function may return an error and a partly valid packet;
* caller must free this packet.
*/
int
parse_packet( IOBUF inp, PACKET *pkt )
{
int skip, rc;
do {
rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0 );
} while( skip );
return rc;
}
/****************
* Like parse packet, but only return packets of the given type.
*/
int
search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos )
{
int skip, rc;
do {
rc = parse( inp, pkt, pkttype, retpos, &skip, NULL, 0 );
} while( skip );
return rc;
}
/****************
* Copy all packets from INP to OUT, thereby removing unused spaces.
*/
int
copy_all_packets( IOBUF inp, IOBUF out )
{
PACKET pkt;
int skip, rc=0;
do {
init_packet(&pkt);
} while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )));
return rc;
}
/****************
* Copy some packets from INP to OUT, thereby removing unused spaces.
* Stop at offset STOPoff (i.e. don't copy packets at this or later offsets)
*/
int
copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff )
{
PACKET pkt;
int skip, rc=0;
do {
if( iobuf_tell(inp) >= stopoff )
return 0;
init_packet(&pkt);
} while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) );
return rc;
}
/****************
* Skip over N packets
*/
int
skip_some_packets( IOBUF inp, unsigned n )
{
int skip, rc=0;
PACKET pkt;
for( ;n && !rc; n--) {
init_packet(&pkt);
rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1 );
}
return rc;
}
/****************
* Parse packet. Set the variable skip points to to 1 if the packet
* should be skipped; this is the case if either there is a
* requested packet type and the parsed packet doesn't match or the
* packet-type is 0, indicating deleted stuff.
* if OUT is not NULL, a special copymode is used.
*/
static int
parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos,
int *skip, IOBUF out, int do_skip )
{
int rc=0, c, ctb, pkttype, lenbytes;
unsigned long pktlen;
byte hdr[8];
int hdrlen;
int new_ctb = 0;
*skip = 0;
assert( !pkt->pkt.generic );
if( retpos )
*retpos = iobuf_tell(inp);
if( (ctb = iobuf_get(inp)) == -1 ) {
rc = -1;
goto leave;
}
hdrlen=0;
hdr[hdrlen++] = ctb;
if( !(ctb & 0x80) ) {
log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
pktlen = 0;
new_ctb = !!(ctb & 0x40);
if( new_ctb ) {
pkttype = ctb & 0x3f;
if( (c = iobuf_get(inp)) == -1 ) {
log_error("%s: 1st length byte missing\n", iobuf_where(inp) );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
hdr[hdrlen++] = c;
if( c < 192 )
pktlen = c;
else if( c < 224 ) {
pktlen = (c - 192) * 256;
if( (c = iobuf_get(inp)) == -1 ) {
log_error("%s: 2nd length byte missing\n", iobuf_where(inp) );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
hdr[hdrlen++] = c;
pktlen += c + 192;
}
else if( c == 255 ) {
pktlen = (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24;
pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 16;
pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 8;
if( (c = iobuf_get(inp)) == -1 ) {
log_error("%s: 4 byte length invalid\n", iobuf_where(inp) );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
pktlen |= (hdr[hdrlen++] = c );
}
else { /* partial body length */
iobuf_set_partial_block_mode(inp, c & 0xff);
pktlen = 0;/* to indicate partial length */
}
}
else {
pkttype = (ctb>>2)&0xf;
lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
if( !lenbytes ) {
pktlen = 0; /* don't know the value */
if( pkttype != PKT_COMPRESSED )
iobuf_set_block_mode(inp, 1);
}
else {
for( ; lenbytes; lenbytes-- ) {
pktlen <<= 8;
pktlen |= hdr[hdrlen++] = iobuf_get_noeof(inp);
}
}
}
if( out && pkttype ) {
if( iobuf_write( out, hdr, hdrlen ) == -1 )
rc = G10ERR_WRITE_FILE;
else
rc = copy_packet(inp, out, pkttype, pktlen );
goto leave;
}
if( do_skip || !pkttype || (reqtype && pkttype != reqtype) ) {
skip_packet(inp, pkttype, pktlen);
*skip = 1;
rc = 0;
goto leave;
}
if( DBG_PACKET )
log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n",
iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" );
pkt->pkttype = pkttype;
rc = G10ERR_UNKNOWN_PACKET; /* default error */
switch( pkttype ) {
case PKT_PUBLIC_KEY:
case PKT_PUBLIC_SUBKEY:
pkt->pkt.public_key = m_alloc_clear(sizeof *pkt->pkt.public_key );
rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
break;
case PKT_SECRET_KEY:
case PKT_SECRET_SUBKEY:
pkt->pkt.secret_key = m_alloc_clear(sizeof *pkt->pkt.secret_key );
rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
break;
case PKT_SYMKEY_ENC:
rc = parse_symkeyenc( inp, pkttype, pktlen, pkt );
break;
case PKT_PUBKEY_ENC:
rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt );
break;
case PKT_SIGNATURE:
pkt->pkt.signature = m_alloc_clear(sizeof *pkt->pkt.signature );
rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature );
break;
case PKT_ONEPASS_SIG:
pkt->pkt.onepass_sig = m_alloc_clear(sizeof *pkt->pkt.onepass_sig );
rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig );
break;
case PKT_USER_ID:
rc = parse_user_id(inp, pkttype, pktlen, pkt );
break;
case PKT_OLD_COMMENT:
case PKT_COMMENT:
rc = parse_comment(inp, pkttype, pktlen, pkt);
break;
case PKT_RING_TRUST:
parse_trust(inp, pkttype, pktlen);
rc = G10ERR_UNKNOWN_PACKET;
break;
case PKT_PLAINTEXT:
rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb );
break;
case PKT_COMPRESSED:
rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb );
break;
case PKT_ENCRYPTED:
rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb );
break;
default:
skip_packet(inp, pkttype, pktlen);
break;
}
leave:
if( !rc && iobuf_error(inp) )
rc = G10ERR_INV_KEYRING;
return rc;
}
static void
dump_hex_line( int c, int *i )
{
if( *i && !(*i%8) ) {
if( *i && !(*i%24) )
printf("\n%4d:", *i );
else
putchar(' ');
}
if( c == -1 )
printf(" EOF" );
else
printf(" %02x", c );
++*i;
}
static int
copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen )
{
int n;
char buf[100];
if( iobuf_in_block_mode(inp) ) {
while( (n = iobuf_read( inp, buf, 100 )) != -1 )
if( iobuf_write(out, buf, n ) )
return G10ERR_WRITE_FILE; /* write error */
}
else if( !pktlen && pkttype == PKT_COMPRESSED ) {
/* compressed packet, copy till EOF */
while( (n = iobuf_read( inp, buf, 100 )) != -1 )
if( iobuf_write(out, buf, n ) )
return G10ERR_WRITE_FILE; /* write error */
}
else {
for( ; pktlen; pktlen -= n ) {
n = pktlen > 100 ? 100 : pktlen;
n = iobuf_read( inp, buf, n );
if( n == -1 )
return G10ERR_READ_FILE;
if( iobuf_write(out, buf, n ) )
return G10ERR_WRITE_FILE; /* write error */
}
}
return 0;
}
static void
skip_packet( IOBUF inp, int pkttype, unsigned long pktlen )
{
if( list_mode ) {
printf(":unknown packet: type %2d, length %lu\n", pkttype, pktlen );
if( pkttype ) {
int c, i=0 ;
printf("dump:");
if( iobuf_in_block_mode(inp) ) {
while( (c=iobuf_get(inp)) != -1 )
dump_hex_line(c, &i);
}
else {
for( ; pktlen; pktlen-- )
dump_hex_line(iobuf_get(inp), &i);
}
putchar('\n');
return;
}
}
skip_rest(inp,pktlen);
}
static void
skip_rest( IOBUF inp, unsigned long pktlen )
{
if( iobuf_in_block_mode(inp) ) {
while( iobuf_get(inp) != -1 )
;
}
else {
for( ; pktlen; pktlen-- )
if( iobuf_get(inp) == -1 )
break;
}
}
static void *
read_rest( IOBUF inp, ulong *r_pktlen )
{
byte *p;
int i;
size_t pktlen = *r_pktlen;
if( iobuf_in_block_mode(inp) ) {
log_error("read_rest: can't store stream data\n");
p = NULL;
}
else {
p = m_alloc( pktlen + 2 );
p[0] = pktlen >> 8;
p[1] = pktlen & 0xff;
for(i=2; pktlen; pktlen--, i++ )
p[i] = iobuf_get(inp);
}
*r_pktlen = 0;
return p;
}
static void *
read_rest2( IOBUF inp, size_t pktlen )
{
byte *p;
int i;
if( iobuf_in_block_mode(inp) ) {
log_error("read_rest: can't store stream data\n");
p = NULL;
}
else {
p = m_alloc( pktlen );
for(i=0; pktlen; pktlen--, i++ )
p[i] = iobuf_get(inp);
}
return p;
}
static int
parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
PKT_symkey_enc *k;
int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
if( pktlen < 4 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
version = iobuf_get_noeof(inp); pktlen--;
if( version != 4 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, version);
goto leave;
}
if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */
log_error("packet(%d) too large\n", pkttype);
goto leave;
}
cipher_algo = iobuf_get_noeof(inp); pktlen--;
s2kmode = iobuf_get_noeof(inp); pktlen--;
hash_algo = iobuf_get_noeof(inp); pktlen--;
switch( s2kmode ) {
case 0: /* simple s2k */
minlen = 0;
break;
case 1: /* salted s2k */
minlen = 8;
break;
case 3: /* iterated+salted s2k */
minlen = 9;
break;
default:
log_error("unknown S2K %d\n", s2kmode );
goto leave;
}
if( minlen > pktlen ) {
log_error("packet with S2K %d too short\n", s2kmode );
goto leave;
}
seskeylen = pktlen - minlen;
k = packet->pkt.symkey_enc = m_alloc_clear( sizeof *packet->pkt.symkey_enc
+ seskeylen - 1 );
k->version = version;
k->cipher_algo = cipher_algo;
k->s2k.mode = s2kmode;
k->s2k.hash_algo = hash_algo;
if( s2kmode == 1 || s2kmode == 3 ) {
for(i=0; i < 8 && pktlen; i++, pktlen-- )
k->s2k.salt[i] = iobuf_get_noeof(inp);
}
if( s2kmode == 3 ) {
k->s2k.count = iobuf_get(inp); pktlen--;
}
k->seskeylen = seskeylen;
for(i=0; i < seskeylen && pktlen; i++, pktlen-- )
k->seskey[i] = iobuf_get_noeof(inp);
assert( !pktlen );
if( list_mode ) {
printf(":symkey enc packet: version %d, cipher %d, s2k %d, hash %d\n",
version, cipher_algo, s2kmode, hash_algo);
if( s2kmode == 1 || s2kmode == 3 ) {
printf("\tsalt ");
for(i=0; i < 8; i++ )
printf("%02x", k->s2k.salt[i]);
if( s2kmode == 3 )
printf(", count %lu\n", (ulong)k->s2k.count );
printf("\n");
}
}
leave:
skip_rest(inp, pktlen);
return 0;
}
static int
parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
unsigned n;
int i, ndata;
PKT_pubkey_enc *k;
k = packet->pkt.pubkey_enc = m_alloc(sizeof *packet->pkt.pubkey_enc );
if( pktlen < 12 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
k->version = iobuf_get_noeof(inp); pktlen--;
if( k->version != 2 && k->version != 3 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, k->version);
goto leave;
}
k->keyid[0] = read_32(inp); pktlen -= 4;
k->keyid[1] = read_32(inp); pktlen -= 4;
k->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
k->throw_keyid = 0; /* only used as flag for build_packet */
if( list_mode )
printf(":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
k->version, k->pubkey_algo, (ulong)k->keyid[0], (ulong)k->keyid[1]);
ndata = pubkey_get_nenc(k->pubkey_algo);
if( !ndata ) {
if( list_mode )
printf("\tunsupported algorithm %d\n", k->pubkey_algo );
unknown_pubkey_warning( k->pubkey_algo );
k->data[0] = NULL; /* no need to store the encrypted data */
}
else {
for( i=0; i < ndata; i++ ) {
n = pktlen;
k->data[i] = mpi_read(inp, &n, 0); pktlen -=n;
if( list_mode ) {
printf("\tdata: ");
mpi_print(stdout, k->data[i], mpi_print_mode );
putchar('\n');
}
}
}
leave:
skip_rest(inp, pktlen);
return 0;
}
static void
dump_sig_subpkt( int hashed, int type, int critical,
const char * buffer, size_t buflen, size_t length )
{
const char *p=NULL;
printf("\t%s%ssubpkt %d len %u (", /*)*/
critical ? "critical ":"",
hashed ? "hashed ":"", type, (unsigned)length );
buffer++;
length--;
if( length > buflen ) {
printf("too short: buffer is only %u)\n", (unsigned)buflen );
return;
}
switch( type ) {
case SIGSUBPKT_SIG_CREATED:
if( length >= 4 )
printf("sig created %s", strtimestamp( buffer_to_u32(buffer) ) );
break;
case SIGSUBPKT_SIG_EXPIRE:
if( length >= 4 )
printf("sig expires %s", strtimestamp( buffer_to_u32(buffer) ) );
break;
case SIGSUBPKT_EXPORTABLE:
- p = "exportable";
+ if( length )
+ printf("%sexportable", *buffer? "":"not ");
break;
case SIGSUBPKT_TRUST:
p = "trust signature";
break;
case SIGSUBPKT_REGEXP:
p = "regular expression";
break;
case SIGSUBPKT_REVOCABLE:
p = "revocable";
break;
case SIGSUBPKT_KEY_EXPIRE:
if( length >= 4 )
printf("key expires %s", strtimestamp( buffer_to_u32(buffer) ) );
break;
case SIGSUBPKT_ARR:
p = "additional recipient request";
break;
case SIGSUBPKT_PREF_SYM:
p = "preferred symmetric algorithms";
break;
case SIGSUBPKT_REV_KEY:
p = "revocation key";
break;
case SIGSUBPKT_ISSUER:
if( length >= 8 )
printf("issuer key ID %08lX%08lX",
(ulong)buffer_to_u32(buffer),
(ulong)buffer_to_u32(buffer+4) );
break;
case SIGSUBPKT_NOTATION:
p = "notation data";
break;
case SIGSUBPKT_PREF_HASH:
p = "preferred hash algorithms";
break;
case SIGSUBPKT_PREF_COMPR:
p = "preferred compression algorithms";
break;
case SIGSUBPKT_KS_FLAGS:
p = "key server preferences";
break;
case SIGSUBPKT_PREF_KS:
p = "preferred key server";
break;
case SIGSUBPKT_PRIMARY_UID:
p = "primary user id";
break;
case SIGSUBPKT_POLICY:
p = "policy URL";
break;
case SIGSUBPKT_KEY_FLAGS:
p = "key flags";
break;
case SIGSUBPKT_SIGNERS_UID:
p = "signer's user id";
break;
case SIGSUBPKT_PRIV_ADD_SIG:
p = "signs additional user id";
break;
default: p = "?"; break;
}
printf("%s)\n", p? p: "");
}
const byte *
parse_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, size_t *ret_n )
{
int buflen;
int type;
int critical;
size_t n;
if( !buffer )
return NULL;
buflen = (*buffer << 8) | buffer[1];
buffer += 2;
for(;;) {
if( !buflen )
return NULL; /* end of packets; not found */
n = *buffer++; buflen--;
if( n == 255 ) {
if( buflen < 4 )
goto too_short;
n = (buffer[0] << 24) | (buffer[1] << 16)
| (buffer[2] << 8) | buffer[3];
buffer += 4;
buflen -= 4;
}
else if( n >= 192 ) {
if( buflen < 2 )
goto too_short;
n = (( n - 192 ) << 8) + *buffer + 192;
buflen--;
}
if( buflen < n )
goto too_short;
type = *buffer;
if( type & 0x80 ) {
type &= 0x7f;
critical = 1;
}
else
critical = 0;
if( reqtype < 0 ) /* list packets */
dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED,
type, critical, buffer, buflen, n );
else if( type == reqtype )
break; /* found */
buffer += n; buflen -=n;
}
buffer++;
n--;
if( n > buflen )
goto too_short;
if( ret_n )
*ret_n = n;
switch( type ) {
case SIGSUBPKT_SIG_CREATED:
case SIGSUBPKT_SIG_EXPIRE:
case SIGSUBPKT_KEY_EXPIRE:
if( n < 4 )
break;
return buffer;
+ case SIGSUBPKT_EXPORTABLE:
+ if( !n )
+ break;
+ return buffer;
case SIGSUBPKT_ISSUER:/* issuer key ID */
if( n < 8 )
break;
return buffer;
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
return buffer;
case SIGSUBPKT_PRIV_ADD_SIG:
/* because we use private data, we check the GNUPG marker */
if( n < 24 )
break;
if( buffer[0] != 'G' || buffer[1] != 'P' || buffer[2] != 'G' )
return NULL;
return buffer+3;
default: BUG(); /* not yet needed */
}
log_error("subpacket of type %d too short\n", type);
return NULL;
too_short:
log_error("buffer shorter than subpacket\n");
return NULL;
}
const byte *
parse_sig_subpkt2( PKT_signature *sig, sigsubpkttype_t reqtype, size_t *ret_n )
{
const byte *p;
p = parse_sig_subpkt( sig->hashed_data, reqtype, ret_n );
if( !p )
p = parse_sig_subpkt( sig->unhashed_data, reqtype, ret_n );
return p;
}
static int
parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_signature *sig )
{
int md5_len=0;
unsigned n;
int is_v4=0;
int rc=0;
int i, ndata;
if( pktlen < 16 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
sig->version = iobuf_get_noeof(inp); pktlen--;
if( sig->version == 4 )
is_v4=1;
else if( sig->version != 2 && sig->version != 3 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, sig->version);
goto leave;
}
if( !is_v4 ) {
md5_len = iobuf_get_noeof(inp); pktlen--;
}
sig->sig_class = iobuf_get_noeof(inp); pktlen--;
if( !is_v4 ) {
sig->timestamp = read_32(inp); pktlen -= 4;
sig->keyid[0] = read_32(inp); pktlen -= 4;
sig->keyid[1] = read_32(inp); pktlen -= 4;
}
sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
sig->digest_algo = iobuf_get_noeof(inp); pktlen--;
if( is_v4 ) { /* read subpackets */
n = read_16(inp); pktlen -= 2; /* length of hashed data */
if( n > 10000 ) {
log_error("signature packet: hashed data too long\n");
rc = G10ERR_INVALID_PACKET;
goto leave;
}
if( n ) {
sig->hashed_data = m_alloc( n + 2 );
sig->hashed_data[0] = n << 8;
sig->hashed_data[1] = n;
if( iobuf_read(inp, sig->hashed_data+2, n ) != n ) {
log_error("premature eof while reading hashed signature data\n");
rc = -1;
goto leave;
}
pktlen -= n;
}
n = read_16(inp); pktlen -= 2; /* length of unhashed data */
if( n > 10000 ) {
log_error("signature packet: unhashed data too long\n");
rc = G10ERR_INVALID_PACKET;
goto leave;
}
if( n ) {
sig->unhashed_data = m_alloc( n + 2 );
sig->unhashed_data[0] = n << 8;
sig->unhashed_data[1] = n;
if( iobuf_read(inp, sig->unhashed_data+2, n ) != n ) {
log_error("premature eof while reading unhashed signature data\n");
rc = -1;
goto leave;
}
pktlen -= n;
}
}
if( pktlen < 5 ) { /* sanity check */
log_error("packet(%d) too short\n", pkttype);
rc = G10ERR_INVALID_PACKET;
goto leave;
}
sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--;
sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--;
if( is_v4 ) { /*extract required information */
const byte *p;
/* FIXME: set sig->flags.unknown_critical is there is a
* critical bit set for packets which are not understood
* It does only make sense for hashed data.
*/
p = parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_SIG_CREATED, NULL );
if( !p )
log_error("signature packet without timestamp\n");
else
sig->timestamp = buffer_to_u32(p);
p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL );
if( !p )
log_error("signature packet without keyid\n");
else {
sig->keyid[0] = buffer_to_u32(p);
sig->keyid[1] = buffer_to_u32(p+4);
}
}
if( list_mode ) {
printf(":signature packet: algo %d, keyid %08lX%08lX\n"
"\tversion %d, created %lu, md5len %d, sigclass %02x\n"
"\tdigest algo %d, begin of digest %02x %02x\n",
sig->pubkey_algo,
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
sig->version, (ulong)sig->timestamp, md5_len, sig->sig_class,
sig->digest_algo,
sig->digest_start[0], sig->digest_start[1] );
if( is_v4 ) {
parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_LIST_HASHED, NULL );
parse_sig_subpkt( sig->unhashed_data,SIGSUBPKT_LIST_UNHASHED, NULL);
}
}
ndata = pubkey_get_nsig(sig->pubkey_algo);
if( !ndata ) {
if( list_mode )
printf("\tunknown algorithm %d\n", sig->pubkey_algo );
unknown_pubkey_warning( sig->pubkey_algo );
/* we store the plain material in data[0], so that we are able
* to write it back with build_packet() */
sig->data[0] = read_rest(inp, &pktlen );
}
else {
for( i=0; i < ndata; i++ ) {
n = pktlen;
sig->data[i] = mpi_read(inp, &n, 0 );
pktlen -=n;
if( list_mode ) {
printf("\tdata: ");
mpi_print(stdout, sig->data[i], mpi_print_mode );
putchar('\n');
}
}
}
leave:
skip_rest(inp, pktlen);
return rc;
}
static int
parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_onepass_sig *ops )
{
int version;
if( pktlen < 13 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
version = iobuf_get_noeof(inp); pktlen--;
if( version != 3 ) {
log_error("onepass_sig with unknown version %d\n", version);
goto leave;
}
ops->sig_class = iobuf_get_noeof(inp); pktlen--;
ops->digest_algo = iobuf_get_noeof(inp); pktlen--;
ops->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
ops->keyid[0] = read_32(inp); pktlen -= 4;
ops->keyid[1] = read_32(inp); pktlen -= 4;
ops->last = iobuf_get_noeof(inp); pktlen--;
if( list_mode )
printf(":onepass_sig packet: keyid %08lX%08lX\n"
"\tversion %d, sigclass %02x, digest %d, pubkey %d, last=%d\n",
(ulong)ops->keyid[0], (ulong)ops->keyid[1],
version, ops->sig_class,
ops->digest_algo, ops->pubkey_algo, ops->last );
leave:
skip_rest(inp, pktlen);
return 0;
}
static int
parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
byte *hdr, int hdrlen, PACKET *pkt )
{
int i, version, algorithm;
unsigned n;
unsigned long timestamp, expiredate;
int npkey, nskey;
int is_v4=0;
int rc=0;
version = iobuf_get_noeof(inp); pktlen--;
if( pkttype == PKT_PUBLIC_SUBKEY && version == '#' ) {
/* early versions of G10 use old PGP comments packets;
* luckily all those comments are started by a hash */
if( list_mode ) {
printf(":rfc1991 comment packet: \"" );
for( ; pktlen; pktlen-- ) {
int c;
c = iobuf_get_noeof(inp);
if( c >= ' ' && c <= 'z' )
putchar(c);
else
printf("\\x%02x", c );
}
printf("\"\n");
}
skip_rest(inp, pktlen);
return 0;
}
else if( version == 4 )
is_v4=1;
else if( version != 2 && version != 3 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, version);
goto leave;
}
if( pktlen < 11 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
timestamp = read_32(inp); pktlen -= 4;
if( is_v4 )
expiredate = 0; /* have to get it from the selfsignature */
else {
unsigned short ndays;
ndays = read_16(inp); pktlen -= 2;
if( ndays )
expiredate = timestamp + ndays * 86400L;
else
expiredate = 0;
}
algorithm = iobuf_get_noeof(inp); pktlen--;
if( list_mode )
printf(":%s key packet:\n"
"\tversion %d, algo %d, created %lu, expires %lu\n",
pkttype == PKT_PUBLIC_KEY? "public" :
pkttype == PKT_SECRET_KEY? "secret" :
pkttype == PKT_PUBLIC_SUBKEY? "public sub" :
pkttype == PKT_SECRET_SUBKEY? "secret sub" : "??",
version, algorithm, timestamp, expiredate );
if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
PKT_secret_key *sk = pkt->pkt.secret_key;
sk->timestamp = timestamp;
sk->expiredate = expiredate;
sk->hdrbytes = hdrlen;
sk->version = version;
sk->pubkey_algo = algorithm;
sk->pubkey_usage = 0; /* not yet used */
}
else {
PKT_public_key *pk = pkt->pkt.public_key;
pk->timestamp = timestamp;
pk->expiredate = expiredate;
pk->hdrbytes = hdrlen;
pk->version = version;
pk->pubkey_algo = algorithm;
pk->pubkey_usage = 0; /* not yet used */
pk->keyid[0] = 0;
pk->keyid[1] = 0;
}
nskey = pubkey_get_nskey( algorithm );
npkey = pubkey_get_npkey( algorithm );
if( !npkey ) {
if( list_mode )
printf("\tunknown algorithm %d\n", algorithm );
unknown_pubkey_warning( algorithm );
}
if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
PKT_secret_key *sk = pkt->pkt.secret_key;
byte temp[8];
if( !npkey ) {
sk->skey[0] = read_rest( inp, &pktlen );
goto leave;
}
for(i=0; i < npkey; i++ ) {
n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
if( list_mode ) {
printf( "\tskey[%d]: ", i);
mpi_print(stdout, sk->skey[i], mpi_print_mode );
putchar('\n');
}
}
sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
if( sk->protect.algo ) {
sk->is_protected = 1;
sk->protect.s2k.count = 0;
if( sk->protect.algo == 255 ) {
if( pktlen < 3 ) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--;
sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--;
switch( sk->protect.s2k.mode ) {
case 1:
case 3:
for(i=0; i < 8 && pktlen; i++, pktlen-- )
temp[i] = iobuf_get_noeof(inp);
memcpy(sk->protect.s2k.salt, temp, 8 );
break;
}
switch( sk->protect.s2k.mode ) {
case 0: if( list_mode ) printf( "\tsimple S2K" );
break;
case 1: if( list_mode ) printf( "\tsalted S2K" );
break;
case 3: if( list_mode ) printf( "\titer+salt S2K" );
break;
default:
if( list_mode )
printf( "\tunknown S2K %d\n",
sk->protect.s2k.mode );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
if( list_mode ) {
printf(", algo: %d, hash: %d",
sk->protect.algo,
sk->protect.s2k.hash_algo );
if( sk->protect.s2k.mode == 1
|| sk->protect.s2k.mode == 3 ) {
printf(", salt: ");
for(i=0; i < 8; i++ )
printf("%02x", sk->protect.s2k.salt[i]);
}
putchar('\n');
}
if( sk->protect.s2k.mode == 3 ) {
if( pktlen < 1 ) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
sk->protect.s2k.count = iobuf_get(inp);
pktlen--;
if( list_mode )
printf("\tprotect count: %lu\n",
(ulong)sk->protect.s2k.count);
}
}
else { /* old version; no S2K, so we set mode to 0, hash MD5 */
sk->protect.s2k.mode = 0;
/* We need a kludge to cope with old GNUPG versions */
sk->protect.s2k.hash_algo =
( sk->protect.algo == CIPHER_ALGO_BLOWFISH160
&& algorithm == PUBKEY_ALGO_ELGAMAL_E ) ?
DIGEST_ALGO_RMD160 : DIGEST_ALGO_MD5;
if( list_mode )
printf( "\tprotect algo: %d (hash algo: %d)\n",
sk->protect.algo, sk->protect.s2k.hash_algo );
}
if( pktlen < 8 ) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
/* fixme: Add support for other blocksizes */
for(i=0; i < 8 && pktlen; i++, pktlen-- )
temp[i] = iobuf_get_noeof(inp);
if( list_mode ) {
printf( "\tprotect IV: ");
for(i=0; i < 8; i++ )
printf(" %02x", temp[i] );
putchar('\n');
}
memcpy(sk->protect.iv, temp, 8 );
}
else
sk->is_protected = 0;
/* It does not make sense to read it into secure memory.
* If the user is so careless, not to protect his secret key,
* we can assume, that he operates an open system :=(.
* So we put the key into secure memory when we unprotect it. */
if( is_v4 && sk->is_protected ){
/* ugly; the length is encrypted too, so we read all
* stuff up to the end of the packet into the first
* skey element */
sk->skey[npkey] = mpi_set_opaque(NULL,
read_rest2(inp, pktlen), pktlen );
pktlen = 0;
if( list_mode ) {
printf("\tencrypted stuff follows\n");
}
}
else { /* v3 method: the mpi length is not encrypted */
for(i=npkey; i < nskey; i++ ) {
n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
if( sk->is_protected )
mpi_set_protect_flag(sk->skey[i]);
if( list_mode ) {
printf( "\tskey[%d]: ", i);
if( sk->is_protected )
printf( "[encrypted]\n");
else {
mpi_print(stdout, sk->skey[i], mpi_print_mode );
putchar('\n');
}
}
}
sk->csum = read_16(inp); pktlen -= 2;
if( list_mode ) {
printf("\tchecksum: %04hx\n", sk->csum);
}
}
}
else {
PKT_public_key *pk = pkt->pkt.public_key;
if( !npkey ) {
pk->pkey[0] = read_rest( inp, &pktlen );
goto leave;
}
for(i=0; i < npkey; i++ ) {
n = pktlen; pk->pkey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
if( list_mode ) {
printf( "\tpkey[%d]: ", i);
mpi_print(stdout, pk->pkey[i], mpi_print_mode );
putchar('\n');
}
}
}
leave:
skip_rest(inp, pktlen);
return rc;
}
static int
parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
byte *p;
packet->pkt.user_id = m_alloc(sizeof *packet->pkt.user_id + pktlen - 1);
packet->pkt.user_id->len = pktlen;
p = packet->pkt.user_id->name;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
if( list_mode ) {
int n = packet->pkt.user_id->len;
printf(":user id packet: \"");
for(p=packet->pkt.user_id->name; n; p++, n-- ) {
if( *p >= ' ' && *p <= 'z' )
putchar(*p);
else
printf("\\x%02x", *p );
}
printf("\"\n");
}
return 0;
}
static int
parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
byte *p;
packet->pkt.comment = m_alloc(sizeof *packet->pkt.comment + pktlen - 1);
packet->pkt.comment->len = pktlen;
p = packet->pkt.comment->data;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
if( list_mode ) {
int n = packet->pkt.comment->len;
printf(":%scomment packet: \"", pkttype == PKT_OLD_COMMENT?
"OpenPGP draft " : "" );
for(p=packet->pkt.comment->data; n; p++, n-- ) {
if( *p >= ' ' && *p <= 'z' )
putchar(*p);
else
printf("\\x%02x", *p );
}
printf("\"\n");
}
return 0;
}
static void
parse_trust( IOBUF inp, int pkttype, unsigned long pktlen )
{
int c;
c = iobuf_get_noeof(inp);
if( list_mode )
printf(":trust packet: flag=%02x\n", c );
}
static int
parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb )
{
int mode, namelen;
PKT_plaintext *pt;
byte *p;
int c, i;
if( pktlen && pktlen < 6 ) {
log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
goto leave;
}
mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
pt = pkt->pkt.plaintext = m_alloc(sizeof *pkt->pkt.plaintext + namelen -1);
pt->new_ctb = new_ctb;
pt->mode = mode;
pt->namelen = namelen;
if( pktlen ) {
for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ )
pt->name[i] = iobuf_get_noeof(inp);
}
else {
for( i=0; i < namelen; i++ )
if( (c=iobuf_get(inp)) == -1 )
break;
else
pt->name[i] = c;
}
pt->timestamp = read_32(inp); if( pktlen) pktlen -= 4;
pt->len = pktlen;
pt->buf = inp;
pktlen = 0;
if( list_mode ) {
printf(":literal data packet:\n"
"\tmode %c, created %lu, name=\"",
mode >= ' ' && mode <'z'? mode : '?',
(ulong)pt->timestamp );
for(p=pt->name,i=0; i < namelen; p++, i++ ) {
if( *p >= ' ' && *p <= 'z' )
putchar(*p);
else
printf("\\x%02x", *p );
}
printf("\",\n\traw data: %lu bytes\n", (ulong)pt->len );
}
leave:
return 0;
}
static int
parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb )
{
PKT_compressed *zd;
/* pktlen is here 0, but data follows
* (this should be the last object in a file or
* the compress algorithm should know the length)
*/
zd = pkt->pkt.compressed = m_alloc(sizeof *pkt->pkt.compressed );
zd->len = 0; /* not yet used */
zd->algorithm = iobuf_get_noeof(inp);
zd->new_ctb = new_ctb;
zd->buf = inp;
if( list_mode )
printf(":compressed packet: algo=%d\n", zd->algorithm);
return 0;
}
static int
parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb )
{
PKT_encrypted *ed;
ed = pkt->pkt.encrypted = m_alloc(sizeof *pkt->pkt.encrypted );
ed->len = pktlen;
ed->buf = NULL;
ed->new_ctb = new_ctb;
if( pktlen && pktlen < 10 ) {
log_error("packet(%d) too short\n", pkttype);
skip_rest(inp, pktlen);
goto leave;
}
if( list_mode ) {
if( pktlen )
printf(":encrypted data packet:\n\tlength: %lu\n", pktlen-10);
else
printf(":encrypted data packet:\n\tlength: unknown\n");
}
ed->buf = inp;
pktlen = 0;
leave:
return 0;
}
diff --git a/g10/sig-check.c b/g10/sig-check.c
index fd476f767..da732c451 100644
--- a/g10/sig-check.c
+++ b/g10/sig-check.c
@@ -1,332 +1,347 @@
/* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#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 md5handle 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 );
return rc;
}
/****************
* 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 )
return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */
cur_time = make_timestamp();
if( pk->timestamp > cur_time ) {
log_info(_("public key created in future (time warp or clock problem)\n"));
return G10ERR_TIME_CONFLICT;
}
if( pk->expiredate && pk->expiredate < cur_time ) {
log_info(_("warning: 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 );
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( (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 ) {
- u32 keyid[2];
+ 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 );
/*md_start_debug(md, "check");*/
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/signal.c b/g10/signal.c
index d89b548b1..8a0f3feee 100644
--- a/g10/signal.c
+++ b/g10/signal.c
@@ -1,123 +1,162 @@
/* signal.c - signal handling
* 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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "options.h"
#include "errors.h"
#include "memory.h"
#include "util.h"
#include "main.h"
#include "ttyio.h"
static volatile int caught_fatal_sig = 0;
static volatile int caught_sigusr1 = 0;
static const char *
signal_name( int signum )
{
#if SYS_SIGLIST_DECLARED
return sys_siglist[signum];
#else
static char buf[20];
sprintf(buf, "signal %d", signum );
return buf;
#endif
}
static RETSIGTYPE
got_fatal_signal( int sig )
{
if( caught_fatal_sig )
raise( sig );
caught_fatal_sig = 1;
fprintf( stderr, "\n%s: %s caught ... exiting\n",
log_get_name(), signal_name(sig) );
secmem_term();
exit( 8 );
}
static RETSIGTYPE
got_usr_signal( int sig )
{
caught_sigusr1 = 1;
}
#ifndef __MINGW32__
static void
do_sigaction( int sig, struct sigaction *nact )
{
struct sigaction oact;
sigaction( sig, NULL, &oact );
if( oact.sa_handler != SIG_IGN )
sigaction( sig, nact, NULL);
}
#endif
void
init_signals()
{
#ifndef __MINGW32__
struct sigaction nact;
nact.sa_handler = got_fatal_signal;
sigemptyset( &nact.sa_mask );
nact.sa_flags = 0;
do_sigaction( SIGINT, &nact );
do_sigaction( SIGHUP, &nact );
do_sigaction( SIGTERM, &nact );
do_sigaction( SIGQUIT, &nact );
do_sigaction( SIGSEGV, &nact );
nact.sa_handler = got_usr_signal;
sigaction( SIGUSR1, &nact, NULL );
#endif
}
void
pause_on_sigusr( int which )
{
#ifndef __MINGW32__
sigset_t mask, oldmask;
assert( which == 1 );
sigemptyset( &mask );
sigaddset( &mask, SIGUSR1 );
sigprocmask( SIG_BLOCK, &mask, &oldmask );
while( !caught_sigusr1 )
sigsuspend( &oldmask );
caught_sigusr1 = 0;
sigprocmask( SIG_UNBLOCK, &mask, NULL );
#endif
}
+
+static void
+do_block( int block )
+{
+ #ifndef __MINGW32__
+ static int is_blocked;
+ static sigset_t oldmask;
+
+ if( block ) {
+ sigset_t newmask;
+
+ if( is_blocked )
+ log_bug("signals are already blocked\n");
+ sigfillset( &newmask );
+ sigprocmask( SIG_BLOCK, &newmask, &oldmask );
+ is_blocked = 1;
+ }
+ else {
+ if( !is_blocked )
+ log_bug("signals are not blocked\n");
+ sigprocmask( SIG_SETMASK, &oldmask, NULL );
+ is_blocked = 0;
+ }
+ #endif /*__MINGW32__*/
+}
+
+
+void
+block_all_signals()
+{
+ do_block(1);
+}
+
+void
+unblock_all_signals()
+{
+ do_block(0);
+}
+
diff --git a/g10/tdbio.c b/g10/tdbio.c
index 1954929ae..f4c97e85e 100644
--- a/g10/tdbio.c
+++ b/g10/tdbio.c
@@ -1,1431 +1,1436 @@
/* tdbio.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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "errors.h"
#include "iobuf.h"
#include "memory.h"
#include "util.h"
#include "options.h"
#include "main.h"
#include "i18n.h"
#include "trustdb.h"
#include "tdbio.h"
/****************
* Yes, this is a very simple implementation. We should really
* use a page aligned buffer and read complete pages.
* To implement a simple trannsaction system, this is sufficient.
*/
typedef struct cache_ctrl_struct *CACHE_CTRL;
struct cache_ctrl_struct {
CACHE_CTRL next;
struct {
unsigned used:1;
unsigned dirty:1;
} flags;
ulong recno;
char data[TRUST_RECORD_LEN];
};
#define MAX_CACHE_ENTRIES_SOFT 200 /* may be increased due while in a */
#define MAX_CACHE_ENTRIES_HARD 1000 /* transaction to this one */
static CACHE_CTRL cache_list;
static int cache_entries;
static int cache_is_dirty;
/* a type used to pass infomation to cmp_krec_fpr */
struct cmp_krec_fpr_struct {
int pubkey_algo;
const char *fpr;
int fprlen;
};
/* a type used to pass infomation to cmp_sdir */
struct cmp_sdir_struct {
int pubkey_algo;
u32 keyid[2];
};
static char *db_name;
static int db_fd = -1;
static int in_transaction;
static void open_db(void);
/*************************************
************* record cache **********
*************************************/
/****************
* Get the data from therecord cache and return a
* pointer into that cache. Caller should copy
* the return data. NULL is returned on a cache miss.
*/
static const char *
get_record_from_cache( ulong recno )
{
CACHE_CTRL r;
for( r = cache_list; r; r = r->next ) {
if( r->flags.used && r->recno == recno )
return r->data;
}
return NULL;
}
static int
write_cache_item( CACHE_CTRL r )
{
int n;
if( lseek( db_fd, r->recno * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
log_error(_("trustdb rec %lu: lseek failed: %s\n"),
r->recno, strerror(errno) );
return G10ERR_WRITE_FILE;
}
n = write( db_fd, r->data, TRUST_RECORD_LEN);
if( n != TRUST_RECORD_LEN ) {
log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"),
r->recno, n, strerror(errno) );
return G10ERR_WRITE_FILE;
}
r->flags.dirty = 0;
return 0;
}
/****************
* Put data into the cache. This function may flush the
* some cache entries if there is not enough space available.
*/
int
put_record_into_cache( ulong recno, const char *data )
{
CACHE_CTRL r, unused;
int dirty_count = 0;
int clean_count = 0;
/* see whether we already cached this one */
for( unused = NULL, r = cache_list; r; r = r->next ) {
if( !r->flags.used ) {
if( !unused )
unused = r;
}
else if( r->recno == recno ) {
if( !r->flags.dirty ) {
/* Hmmm: should we use a a copy and compare? */
if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) {
r->flags.dirty = 1;
cache_is_dirty = 1;
}
}
memcpy( r->data, data, TRUST_RECORD_LEN );
return 0;
}
if( r->flags.used ) {
if( r->flags.dirty )
dirty_count++;
else
clean_count++;
}
}
/* not in the cache: add a new entry */
if( unused ) { /* reuse this entry */
r = unused;
r->flags.used = 1;
r->recno = recno;
memcpy( r->data, data, TRUST_RECORD_LEN );
r->flags.dirty = 1;
cache_is_dirty = 1;
cache_entries++;
return 0;
}
/* see whether we reached the limit */
if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */
r = m_alloc( sizeof *r );
r->flags.used = 1;
r->recno = recno;
memcpy( r->data, data, TRUST_RECORD_LEN );
r->flags.dirty = 1;
r->next = cache_list;
cache_list = r;
cache_is_dirty = 1;
cache_entries++;
return 0;
}
/* cache is full: discard some clean entries */
if( clean_count ) {
int n = clean_count / 3; /* discard a third of the clean entries */
if( !n )
n = 1;
for( unused = NULL, r = cache_list; r; r = r->next ) {
if( r->flags.used && !r->flags.dirty ) {
if( !unused )
unused = r;
r->flags.used = 0;
cache_entries--;
if( !--n )
break;
}
}
assert( unused );
r = unused;
r->flags.used = 1;
r->recno = recno;
memcpy( r->data, data, TRUST_RECORD_LEN );
r->flags.dirty = 1;
cache_is_dirty = 1;
cache_entries++;
return 0;
}
/* no clean entries: have to flush some dirty entries */
if( in_transaction ) {
/* but we can't do this while in a transaction
* we increase the cache size instead */
if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */
if( !(cache_entries % 100) )
log_info("increasing tdbio cache size\n");
r = m_alloc( sizeof *r );
r->flags.used = 1;
r->recno = recno;
memcpy( r->data, data, TRUST_RECORD_LEN );
r->flags.dirty = 1;
r->next = cache_list;
cache_list = r;
cache_is_dirty = 1;
cache_entries++;
return 0;
}
log_info("hard cache size limit reached\n");
return G10ERR_RESOURCE_LIMIT;
}
if( dirty_count ) {
int n = dirty_count / 5; /* discard some dirty entries */
if( !n )
n = 1;
for( unused = NULL, r = cache_list; r; r = r->next ) {
if( r->flags.used && r->flags.dirty ) {
int rc = write_cache_item( r );
if( rc )
return rc;
if( !unused )
unused = r;
r->flags.used = 0;
cache_entries--;
if( !--n )
break;
}
}
assert( unused );
r = unused;
r->flags.used = 1;
r->recno = recno;
memcpy( r->data, data, TRUST_RECORD_LEN );
r->flags.dirty = 1;
cache_is_dirty = 1;
cache_entries++;
return 0;
}
BUG();
}
int
tdbio_is_dirty()
{
return cache_is_dirty;
}
/****************
* Flush the cache. This cannot be used while in a transaction.
*/
int
tdbio_sync()
{
CACHE_CTRL r;
if( in_transaction )
log_bug("tdbio: syncing while in transaction\n");
if( !cache_is_dirty )
return 0;
for( r = cache_list; r; r = r->next ) {
if( r->flags.used && r->flags.dirty ) {
int rc = write_cache_item( r );
if( rc )
return rc;
}
}
cache_is_dirty = 0;
return 0;
}
/****************
* Simple transactions system:
* Everything between begin_transaction and end/cancel_transaction
* is not immediatly written but at the time of end_transaction.
*
*/
int
tdbio_begin_transaction()
{
int rc;
if( in_transaction )
log_bug("tdbio: nested transactions\n");
/* flush everything out */
rc = tdbio_sync();
if( rc )
return rc;
in_transaction = 1;
return 0;
}
int
tdbio_end_transaction()
{
+ int rc;
+
if( !in_transaction )
log_bug("tdbio: no active transaction\n");
+ block_all_signals();
in_transaction = 0;
- return tdbio_sync();
+ rc = tdbio_sync();
+ unblock_all_signals();
+ return rc;
}
int
tdbio_cancel_transaction()
{
CACHE_CTRL r;
if( !in_transaction )
log_bug("tdbio: no active transaction\n");
/* remove all dirty marked entries, so that the original ones
* are read back the next time */
if( cache_is_dirty ) {
for( r = cache_list; r; r = r->next ) {
if( r->flags.used && r->flags.dirty ) {
r->flags.used = 0;
cache_entries--;
}
}
cache_is_dirty = 0;
}
in_transaction = 0;
return 0;
}
/********************************************************
**************** cached I/O functions ******************
********************************************************/
int
tdbio_set_dbname( const char *new_dbname, int create )
{
char *fname;
fname = new_dbname? m_strdup( new_dbname )
: make_filename(opt.homedir, "trustdb.gpg", NULL );
if( access( fname, R_OK ) ) {
if( errno != ENOENT ) {
log_error( _("%s: can't access: %s\n"), fname, strerror(errno) );
m_free(fname);
return G10ERR_TRUSTDB;
}
if( create ) {
FILE *fp;
TRUSTREC rec;
int rc;
char *p = strrchr( fname, '/' );
assert(p);
*p = 0;
if( access( fname, F_OK ) ) {
if( strlen(fname) >= 7
&& !strcmp(fname+strlen(fname)-7, "/.gnupg" ) ) {
#if __MINGW32__
if( mkdir( fname ) )
#else
if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) )
#endif
log_fatal( _("%s: can't create directory: %s\n"),
fname, strerror(errno) );
else
log_info( _("%s: directory created\n"), fname );
copy_options_file( fname );
}
else
log_fatal( _("%s: directory does not exist!\n"), fname );
}
*p = '/';
fp =fopen( fname, "wb" );
if( !fp )
log_fatal( _("%s: can't create: %s\n"), fname, strerror(errno) );
fclose(fp);
m_free(db_name);
db_name = fname;
#ifdef __MINGW32__
db_fd = open( db_name, O_RDWR | O_BINARY );
#else
db_fd = open( db_name, O_RDWR );
#endif
if( db_fd == -1 )
log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) );
memset( &rec, 0, sizeof rec );
rec.r.ver.version = 2;
rec.r.ver.created = make_timestamp();
rec.rectype = RECTYPE_VER;
rec.recnum = 0;
rc = tdbio_write_record( &rec );
if( !rc )
tdbio_sync();
if( rc )
log_fatal( _("%s: failed to create version record: %s"),
fname, g10_errstr(rc));
/* and read again to check that we are okay */
if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
log_fatal( _("%s: invalid trust-db created\n"), db_name );
log_info(_("%s: trust-db created\n"), db_name);
return 0;
}
}
m_free(db_name);
db_name = fname;
return 0;
}
const char *
tdbio_get_dbname()
{
return db_name;
}
static void
open_db()
{
TRUSTREC rec;
assert( db_fd == -1 );
#ifdef __MINGW32__
db_fd = open( db_name, O_RDWR | O_BINARY );
#else
db_fd = open( db_name, O_RDWR );
#endif
if( db_fd == -1 )
log_fatal( _("%s: can't open: %s\n"), db_name, strerror(errno) );
if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
log_fatal( _("%s: invalid trust-db\n"), db_name );
/* fixme: check ->locked and other stuff */
}
/****************
* Make a hashtable: type 0 = key hash, 1 = sdir hash
*/
static void
create_hashtable( TRUSTREC *vr, int type )
{
TRUSTREC rec;
off_t offset;
ulong recnum;
int i, n, rc;
offset = lseek( db_fd, 0, SEEK_END );
if( offset == -1 )
log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
recnum = offset / TRUST_RECORD_LEN;
assert(recnum); /* this is will never be the first record */
if( !type )
vr->r.ver.keyhashtbl = recnum;
else
vr->r.ver.sdirhashtbl = recnum;
/* Now write the records */
n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD;
for(i=0; i < n; i++, recnum++ ) {
memset( &rec, 0, sizeof rec );
rec.rectype = RECTYPE_HTBL;
rec.recnum = recnum;
rc = tdbio_write_record( &rec );
if( rc )
log_fatal( _("%s: failed to create hashtable: %s\n"),
db_name, g10_errstr(rc));
}
/* update the version record */
rc = tdbio_write_record( vr );
if( !rc )
rc = tdbio_sync();
if( rc )
log_fatal( _("%s: error updating version record: %s\n"),
db_name, g10_errstr(rc));
}
/****************
* Return the record number of the keyhash tbl or create a new one.
*/
static ulong
get_keyhashrec()
{
static ulong keyhashtbl; /* record number of the key hashtable */
if( !keyhashtbl ) {
TRUSTREC vr;
int rc;
rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
if( rc )
log_fatal( _("%s: error reading version record: %s\n"),
db_name, g10_errstr(rc) );
if( !vr.r.ver.keyhashtbl )
create_hashtable( &vr, 0 );
keyhashtbl = vr.r.ver.keyhashtbl;
}
return keyhashtbl;
}
/****************
* Return the record number of the shadow direcory hash table
* or create a new one.
*/
static ulong
get_sdirhashrec()
{
static ulong sdirhashtbl; /* record number of the hashtable */
if( !sdirhashtbl ) {
TRUSTREC vr;
int rc;
rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
if( rc )
log_fatal( _("%s: error reading version record: %s\n"),
db_name, g10_errstr(rc) );
if( !vr.r.ver.sdirhashtbl )
create_hashtable( &vr, 1 );
sdirhashtbl = vr.r.ver.sdirhashtbl;
}
return sdirhashtbl;
}
/****************
* Update a hashtable.
* table gives the start of the table, key and keylen is the key,
* newrecnum is the record number to insert.
*/
static int
upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum )
{
TRUSTREC lastrec, rec;
ulong hashrec, item;
int msb;
int level=0;
int rc, i;
hashrec = table;
next_level:
msb = key[level];
hashrec += msb / ITEMS_PER_HTBL_RECORD;
rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL );
if( rc ) {
log_error( db_name, "upd_hashtable: read failed: %s\n",
g10_errstr(rc) );
return rc;
}
item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
if( !item ) { /* insert a new item into the hash table */
rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum;
rc = tdbio_write_record( &rec );
if( rc ) {
log_error( db_name, "upd_hashtable: write htbl failed: %s\n",
g10_errstr(rc) );
return rc;
}
}
else if( item != newrecnum ) { /* must do an update */
lastrec = rec;
rc = tdbio_read_record( item, &rec, 0 );
if( rc ) {
log_error( "upd_hashtable: read item failed: %s\n",
g10_errstr(rc) );
return rc;
}
if( rec.rectype == RECTYPE_HTBL ) {
hashrec = item;
level++;
if( level >= keylen ) {
log_error( "hashtable has invalid indirections.\n");
return G10ERR_TRUSTDB;
}
goto next_level;
}
else if( rec.rectype == RECTYPE_HLST ) { /* extend list */
/* see whether the key is already in this list */
for(;;) {
for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
if( rec.r.hlst.rnum[i] == newrecnum ) {
return 0; /* okay, already in the list */
}
}
if( rec.r.hlst.next ) {
rc = tdbio_read_record( rec.r.hlst.next,
&rec, RECTYPE_HLST);
if( rc ) {
log_error( "scan keyhashtbl read hlst failed: %s\n",
g10_errstr(rc) );
return rc;
}
}
else
break; /* not there */
}
/* find the next free entry and put it in */
for(;;) {
for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
if( !rec.r.hlst.rnum[i] ) {
rec.r.hlst.rnum[i] = newrecnum;
rc = tdbio_write_record( &rec );
if( rc )
log_error( "upd_hashtable: write hlst failed: %s\n",
g10_errstr(rc) );
return rc; /* done */
}
}
if( rec.r.hlst.next ) {
rc = tdbio_read_record( rec.r.hlst.next,
&rec, RECTYPE_HLST );
if( rc ) {
log_error( "upd_hashtable: read hlst failed: %s\n",
g10_errstr(rc) );
return rc;
}
}
else { /* add a new list record */
rec.r.hlst.next = item = tdbio_new_recnum();
rc = tdbio_write_record( &rec );
if( rc ) {
log_error( "upd_hashtable: write hlst failed: %s\n",
g10_errstr(rc) );
return rc;
}
memset( &rec, 0, sizeof rec );
rec.rectype = RECTYPE_HLST;
rec.recnum = item;
rec.r.hlst.rnum[0] = newrecnum;
rc = tdbio_write_record( &rec );
if( rc )
log_error( "upd_hashtable: write ext hlst failed: %s\n",
g10_errstr(rc) );
return rc; /* done */
}
} /* end loop over hlst slots */
}
else if( rec.rectype == RECTYPE_KEY
|| rec.rectype == RECTYPE_DIR
|| rec.rectype == RECTYPE_SDIR ) { /* insert a list record */
if( rec.recnum == newrecnum ) {
return 0;
}
item = rec.recnum; /* save number of key record */
memset( &rec, 0, sizeof rec );
rec.rectype = RECTYPE_HLST;
rec.recnum = tdbio_new_recnum();
rec.r.hlst.rnum[0] = item; /* old keyrecord */
rec.r.hlst.rnum[1] = newrecnum; /* and new one */
rc = tdbio_write_record( &rec );
if( rc ) {
log_error( "upd_hashtable: write new hlst failed: %s\n",
g10_errstr(rc) );
return rc;
}
/* update the hashtable record */
lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum;
rc = tdbio_write_record( &lastrec );
if( rc )
log_error( "upd_hashtable: update htbl failed: %s\n",
g10_errstr(rc) );
return rc; /* ready */
}
else {
log_error( "hashtbl %lu points to an invalid record\n",
item);
return G10ERR_TRUSTDB;
}
}
return 0;
}
/****************
* Lookup a record via the hashtable tablewith key/keylen and return the
* result in rec. cmp() should return if the record is the desired one.
* Returns -1 if not found, 0 if found or another errocode
*/
static int
lookup_hashtable( ulong table, const byte *key, size_t keylen,
int (*cmpfnc)(void*, const TRUSTREC *), void *cmpdata,
TRUSTREC *rec )
{
int rc;
ulong hashrec, item;
int msb;
int level=0;
hashrec = table;
next_level:
msb = key[level];
hashrec += msb / ITEMS_PER_HTBL_RECORD;
rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL );
if( rc ) {
log_error( db_name, "lookup_hashtable failed: %s\n", g10_errstr(rc) );
return rc;
}
item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
if( !item )
return -1; /* not found */
rc = tdbio_read_record( item, rec, 0 );
if( rc ) {
log_error( db_name, "hashtable read failed: %s\n", g10_errstr(rc) );
return rc;
}
if( rec->rectype == RECTYPE_HTBL ) {
hashrec = item;
level++;
if( level >= keylen ) {
log_error( db_name, "hashtable has invalid indirections\n");
return G10ERR_TRUSTDB;
}
goto next_level;
}
else if( rec->rectype == RECTYPE_HLST ) {
for(;;) {
int i;
for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
if( rec->r.hlst.rnum[i] ) {
TRUSTREC tmp;
rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 );
if( rc ) {
log_error( "lookup_hashtable: read item failed: %s\n",
g10_errstr(rc) );
return rc;
}
if( (*cmpfnc)( cmpdata, &tmp ) ) {
*rec = tmp;
return 0;
}
}
}
if( rec->r.hlst.next ) {
rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST );
if( rc ) {
log_error( "lookup_hashtable: read hlst failed: %s\n",
g10_errstr(rc) );
return rc;
}
}
else
return -1; /* not found */
}
}
if( (*cmpfnc)( cmpdata, rec ) )
return 0; /* really found */
return -1; /* no: not found */
}
/****************
* Update the key hashtbl or create the table if it does not exist
*/
static int
update_keyhashtbl( TRUSTREC *kr )
{
return upd_hashtable( get_keyhashrec(),
kr->r.key.fingerprint,
kr->r.key.fingerprint_len, kr->recnum );
}
/****************
* Update the shadow dir hashtbl or create the table if it does not exist
*/
static int
update_sdirhashtbl( TRUSTREC *sr )
{
byte key[8];
u32tobuf( key , sr->r.sdir.keyid[0] );
u32tobuf( key+4 , sr->r.sdir.keyid[1] );
return upd_hashtable( get_sdirhashrec(), key, 8, sr->recnum );
}
void
tdbio_dump_record( TRUSTREC *rec, FILE *fp )
{
int i;
ulong rnum = rec->recnum;
byte *p;
fprintf(fp, "rec %5lu, ", rnum );
switch( rec->rectype ) {
case 0: fprintf(fp, "blank\n");
break;
case RECTYPE_VER: fprintf(fp, "version, kd=%lu, sd=%lu, free=%lu\n",
rec->r.ver.keyhashtbl, rec->r.ver.sdirhashtbl,
rec->r.ver.firstfree );
break;
case RECTYPE_FREE: fprintf(fp, "free, next=%lu\n", rec->r.free.next );
break;
case RECTYPE_DIR:
fprintf(fp, "dir %lu, keys=%lu, uids=%lu, cach=%lu, ot=%02x",
rec->r.dir.lid,
rec->r.dir.keylist,
rec->r.dir.uidlist,
rec->r.dir.cacherec,
rec->r.dir.ownertrust );
if( rec->r.dir.dirflags & DIRF_CHECKED ) {
if( rec->r.dir.dirflags & DIRF_VALID )
fputs(", valid", fp );
if( rec->r.dir.dirflags & DIRF_EXPIRED )
fputs(", expired", fp );
if( rec->r.dir.dirflags & DIRF_REVOKED )
fputs(", revoked", fp );
}
putc('\n', fp);
break;
case RECTYPE_KEY:
fprintf(fp, "key %lu, next=%lu, algo=%d, ",
rec->r.key.lid,
rec->r.key.next,
rec->r.key.pubkey_algo );
for(i=0; i < rec->r.key.fingerprint_len; i++ )
fprintf(fp, "%02X", rec->r.key.fingerprint[i] );
if( rec->r.key.keyflags & KEYF_CHECKED ) {
if( rec->r.key.keyflags & KEYF_VALID )
fputs(", valid", fp );
if( rec->r.key.keyflags & KEYF_EXPIRED )
fputs(", expired", fp );
if( rec->r.key.keyflags & KEYF_REVOKED )
fputs(", revoked", fp );
}
putc('\n', fp);
break;
case RECTYPE_UID:
fprintf(fp, "uid %lu, next=%lu, pref=%lu, sig=%lu, hash=%02X%02X",
rec->r.uid.lid,
rec->r.uid.next,
rec->r.uid.prefrec,
rec->r.uid.siglist,
rec->r.uid.namehash[18], rec->r.uid.namehash[19]);
if( rec->r.uid.uidflags & UIDF_CHECKED ) {
if( rec->r.uid.uidflags & UIDF_VALID )
fputs(", valid", fp );
if( rec->r.uid.uidflags & UIDF_REVOKED )
fputs(", revoked", fp );
}
putc('\n', fp);
break;
case RECTYPE_PREF:
fprintf(fp, "pref %lu, next=%lu,",
rec->r.pref.lid, rec->r.pref.next);
for(i=0,p=rec->r.pref.data; i < ITEMS_PER_PREF_RECORD; i+=2,p+=2 ) {
if( *p )
fprintf(fp, " %c%d", *p == PREFTYPE_SYM ? 'S' :
*p == PREFTYPE_HASH ? 'H' :
*p == PREFTYPE_COMPR ? 'Z' : '?', p[1]);
}
putc('\n', fp);
break;
case RECTYPE_SIG:
fprintf(fp, "sig %lu, next=%lu,",
rec->r.sig.lid, rec->r.sig.next );
for(i=0; i < SIGS_PER_RECORD; i++ ) {
if( rec->r.sig.sig[i].lid ) {
fprintf(fp, " %lu:", rec->r.sig.sig[i].lid );
if( rec->r.sig.sig[i].flag & SIGF_CHECKED ) {
fprintf(fp,"%c%c%c",
(rec->r.sig.sig[i].flag & SIGF_VALID) ? 'V':'-',
(rec->r.sig.sig[i].flag & SIGF_EXPIRED) ? 'E':'-',
(rec->r.sig.sig[i].flag & SIGF_REVOKED) ? 'R':'-');
}
else if( rec->r.sig.sig[i].flag & SIGF_NOPUBKEY)
fputs("?--", fp);
else
fputs("---", fp);
}
}
putc('\n', fp);
break;
case RECTYPE_SDIR:
fprintf(fp, "sdir %lu, keyid=%08lX%08lX, algo=%d, hint=%lu\n",
rec->r.sdir.lid,
(ulong)rec->r.sdir.keyid[0],
(ulong)rec->r.sdir.keyid[1],
rec->r.sdir.pubkey_algo,
(ulong)rec->r.sdir.hintlist );
break;
case RECTYPE_CACH:
fprintf(fp, "cach\n");
break;
case RECTYPE_HTBL:
fprintf(fp, "htbl,");
for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
fprintf(fp, " %lu", rec->r.htbl.item[i] );
putc('\n', fp);
break;
case RECTYPE_HLST:
fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
putc('\n', fp);
break;
default:
fprintf(fp, "unknown type %d\n", rec->rectype );
break;
}
}
/****************
* read the record with number recnum
* returns: -1 on error, 0 on success
*/
int
tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
{
byte readbuf[TRUST_RECORD_LEN];
const byte *buf, *p;
int rc = 0;
int n, i;
if( db_fd == -1 )
open_db();
buf = get_record_from_cache( recnum );
if( !buf ) {
if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
return G10ERR_READ_FILE;
}
n = read( db_fd, readbuf, TRUST_RECORD_LEN);
if( !n ) {
return -1; /* eof */
}
else if( n != TRUST_RECORD_LEN ) {
log_error(_("trustdb: read failed (n=%d): %s\n"), n,
strerror(errno) );
return G10ERR_READ_FILE;
}
buf = readbuf;
}
rec->recnum = recnum;
rec->dirty = 0;
p = buf;
rec->rectype = *p++;
if( expected && rec->rectype != expected ) {
log_error("%lu: read expected rec type %d, got %d\n",
recnum, expected, rec->rectype );
return G10ERR_TRUSTDB;
}
p++; /* skip reserved byte */
switch( rec->rectype ) {
case 0: /* unused (free) record */
break;
case RECTYPE_VER: /* version record */
if( memcmp(buf+1, "gpg", 3 ) ) {
log_error( _("%s: not a trustdb file\n"), db_name );
rc = G10ERR_TRUSTDB;
}
p += 2; /* skip "pgp" */
rec->r.ver.version = *p++;
p += 3; /* reserved bytes */
p += 4; /* lock flags */
rec->r.ver.created = buftoulong(p); p += 4;
rec->r.ver.modified = buftoulong(p); p += 4;
rec->r.ver.validated= buftoulong(p); p += 4;
rec->r.ver.keyhashtbl=buftoulong(p); p += 4;
rec->r.ver.firstfree =buftoulong(p); p += 4;
rec->r.ver.sdirhashtbl =buftoulong(p); p += 4;
if( recnum ) {
log_error( _("%s: version record with recnum %lu\n"), db_name,
(ulong)recnum );
rc = G10ERR_TRUSTDB;
}
else if( rec->r.ver.version != 2 ) {
log_error( _("%s: invalid file version %d\n"), db_name,
rec->r.ver.version );
rc = G10ERR_TRUSTDB;
}
break;
case RECTYPE_FREE:
rec->r.free.next = buftoulong(p); p += 4;
break;
case RECTYPE_DIR: /*directory record */
rec->r.dir.lid = buftoulong(p); p += 4;
rec->r.dir.keylist = buftoulong(p); p += 4;
rec->r.dir.uidlist = buftoulong(p); p += 4;
rec->r.dir.cacherec = buftoulong(p); p += 4;
rec->r.dir.ownertrust = *p++;
rec->r.dir.dirflags = *p++;
if( rec->r.dir.lid != recnum ) {
log_error( "%s: dir LID != recnum (%lu,%lu)\n",
db_name, rec->r.dir.lid, (ulong)recnum );
rc = G10ERR_TRUSTDB;
}
break;
case RECTYPE_KEY: /* public key record */
rec->r.key.lid = buftoulong(p); p += 4;
rec->r.key.next = buftoulong(p); p += 4;
p += 7;
rec->r.key.keyflags = *p++;
rec->r.key.pubkey_algo = *p++;
rec->r.key.fingerprint_len = *p++;
if( rec->r.key.fingerprint_len < 1 || rec->r.key.fingerprint_len > 20 )
rec->r.key.fingerprint_len = 20;
memcpy( rec->r.key.fingerprint, p, 20);
break;
case RECTYPE_UID: /* user id record */
rec->r.uid.lid = buftoulong(p); p += 4;
rec->r.uid.next = buftoulong(p); p += 4;
rec->r.uid.prefrec = buftoulong(p); p += 4;
rec->r.uid.siglist = buftoulong(p); p += 4;
rec->r.uid.uidflags = *p++;
p ++;
memcpy( rec->r.uid.namehash, p, 20);
break;
case RECTYPE_PREF: /* preference record */
rec->r.pref.lid = buftoulong(p); p += 4;
rec->r.pref.next = buftoulong(p); p += 4;
memcpy( rec->r.pref.data, p, 30 );
break;
case RECTYPE_SIG:
rec->r.sig.lid = buftoulong(p); p += 4;
rec->r.sig.next = buftoulong(p); p += 4;
for(i=0; i < SIGS_PER_RECORD; i++ ) {
rec->r.sig.sig[i].lid = buftoulong(p); p += 4;
rec->r.sig.sig[i].flag = *p++;
}
break;
case RECTYPE_SDIR: /* shadow directory record */
rec->r.sdir.lid = buftoulong(p); p += 4;
rec->r.sdir.keyid[0]= buftou32(p); p += 4;
rec->r.sdir.keyid[1]= buftou32(p); p += 4;
rec->r.sdir.pubkey_algo = *p++;
p += 3;
rec->r.sdir.hintlist = buftoulong(p);
if( rec->r.sdir.lid != recnum ) {
log_error( "%s: sdir LID != recnum (%lu,%lu)\n",
db_name, rec->r.sdir.lid, (ulong)recnum );
rc = G10ERR_TRUSTDB;
}
break;
case RECTYPE_CACH: /* cache record */
rec->r.cache.lid = buftoulong(p); p += 4;
memcpy(rec->r.cache.blockhash, p, 20); p += 20;
rec->r.cache.trustlevel = *p++;
break;
case RECTYPE_HTBL:
for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
rec->r.htbl.item[i] = buftoulong(p); p += 4;
}
break;
case RECTYPE_HLST:
rec->r.hlst.next = buftoulong(p); p += 4;
for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
rec->r.hlst.rnum[i] = buftoulong(p); p += 4;
}
break;
default:
log_error( "%s: invalid record type %d at recnum %lu\n",
db_name, rec->rectype, (ulong)recnum );
rc = G10ERR_TRUSTDB;
break;
}
return rc;
}
/****************
* Write the record at RECNUM
*/
int
tdbio_write_record( TRUSTREC *rec )
{
byte buf[TRUST_RECORD_LEN], *p;
int rc = 0;
int i;
ulong recnum = rec->recnum;
if( db_fd == -1 )
open_db();
memset(buf, 0, TRUST_RECORD_LEN);
p = buf;
*p++ = rec->rectype; p++;
switch( rec->rectype ) {
case 0: /* unused record */
break;
case RECTYPE_VER: /* version record */
if( recnum )
BUG();
memcpy(p-1, "gpg", 3 ); p += 2;
*p++ = rec->r.ver.version;
p += 7; /* skip reserved bytes and lock flags */
ulongtobuf(p, rec->r.ver.created); p += 4;
ulongtobuf(p, rec->r.ver.modified); p += 4;
ulongtobuf(p, rec->r.ver.validated); p += 4;
ulongtobuf(p, rec->r.ver.keyhashtbl); p += 4;
ulongtobuf(p, rec->r.ver.firstfree ); p += 4;
ulongtobuf(p, rec->r.ver.sdirhashtbl ); p += 4;
break;
case RECTYPE_FREE:
ulongtobuf(p, rec->r.free.next); p += 4;
break;
case RECTYPE_DIR: /*directory record */
ulongtobuf(p, rec->r.dir.lid); p += 4;
ulongtobuf(p, rec->r.dir.keylist); p += 4;
ulongtobuf(p, rec->r.dir.uidlist); p += 4;
ulongtobuf(p, rec->r.dir.cacherec); p += 4;
*p++ = rec->r.dir.ownertrust;
*p++ = rec->r.dir.dirflags;
assert( rec->r.dir.lid == recnum );
break;
case RECTYPE_KEY:
ulongtobuf(p, rec->r.key.lid); p += 4;
ulongtobuf(p, rec->r.key.next); p += 4;
p += 7;
*p++ = rec->r.key.keyflags;
*p++ = rec->r.key.pubkey_algo;
*p++ = rec->r.key.fingerprint_len;
memcpy( p, rec->r.key.fingerprint, 20); p += 20;
break;
case RECTYPE_UID: /* user id record */
ulongtobuf(p, rec->r.uid.lid); p += 4;
ulongtobuf(p, rec->r.uid.next); p += 4;
ulongtobuf(p, rec->r.uid.prefrec); p += 4;
ulongtobuf(p, rec->r.uid.siglist); p += 4;
*p++ = rec->r.uid.uidflags;
p++;
memcpy( p, rec->r.uid.namehash, 20 ); p += 20;
break;
case RECTYPE_PREF:
ulongtobuf(p, rec->r.pref.lid); p += 4;
ulongtobuf(p, rec->r.pref.next); p += 4;
memcpy( p, rec->r.pref.data, 30 );
break;
case RECTYPE_SIG:
ulongtobuf(p, rec->r.sig.lid); p += 4;
ulongtobuf(p, rec->r.sig.next); p += 4;
for(i=0; i < SIGS_PER_RECORD; i++ ) {
ulongtobuf(p, rec->r.sig.sig[i].lid); p += 4;
*p++ = rec->r.sig.sig[i].flag;
}
break;
case RECTYPE_SDIR:
ulongtobuf( p, rec->r.sdir.lid); p += 4;
u32tobuf( p, rec->r.sdir.keyid[0] ); p += 4;
u32tobuf( p, rec->r.sdir.keyid[1] ); p += 4;
*p++ = rec->r.sdir.pubkey_algo;
p += 3;
ulongtobuf( p, rec->r.sdir.hintlist );
break;
case RECTYPE_CACH:
ulongtobuf(p, rec->r.cache.lid); p += 4;
memcpy(p, rec->r.cache.blockhash, 20); p += 20;
*p++ = rec->r.cache.trustlevel;
break;
case RECTYPE_HTBL:
for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
}
break;
case RECTYPE_HLST:
ulongtobuf( p, rec->r.hlst.next); p += 4;
for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
}
break;
default:
BUG();
}
rc = put_record_into_cache( recnum, buf );
if( rc )
;
if( rec->rectype == RECTYPE_KEY )
rc = update_keyhashtbl( rec );
else if( rec->rectype == RECTYPE_SDIR )
rc = update_sdirhashtbl( rec );
return rc;
}
int
tdbio_delete_record( ulong recnum )
{
TRUSTREC vr, rec;
int rc;
rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
if( rc )
log_fatal( _("%s: error reading version record: %s\n"),
db_name, g10_errstr(rc) );
rec.recnum = recnum;
rec.rectype = RECTYPE_FREE;
rec.r.free.next = vr.r.ver.firstfree;
vr.r.ver.firstfree = recnum;
rc = tdbio_write_record( &rec );
if( !rc )
rc = tdbio_write_record( &vr );
return rc;
}
/****************
* create a new record and return its record number
*/
ulong
tdbio_new_recnum()
{
off_t offset;
ulong recnum;
TRUSTREC vr, rec;
int rc;
/* look for unused records */
rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
if( rc )
log_fatal( _("%s: error reading version record: %s\n"),
db_name, g10_errstr(rc) );
if( vr.r.ver.firstfree ) {
recnum = vr.r.ver.firstfree;
rc = tdbio_read_record( recnum, &rec, RECTYPE_FREE );
if( rc ) {
log_error( _("%s: error reading free record: %s\n"),
db_name, g10_errstr(rc) );
return rc;
}
/* update dir record */
vr.r.ver.firstfree = rec.r.free.next;
rc = tdbio_write_record( &vr );
if( rc ) {
log_error( _("%s: error writing dir record: %s\n"),
db_name, g10_errstr(rc) );
return rc;
}
/*zero out the new record */
memset( &rec, 0, sizeof rec );
rec.rectype = 0; /* unused record */
rec.recnum = recnum;
rc = tdbio_write_record( &rec );
if( rc )
log_fatal(_("%s: failed to zero a record: %s\n"),
db_name, g10_errstr(rc));
}
else { /* not found, append a new record */
offset = lseek( db_fd, 0, SEEK_END );
if( offset == -1 )
log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
recnum = offset / TRUST_RECORD_LEN;
assert(recnum); /* this is will never be the first record */
/* we must write a record, so that the next call to this function
* returns another recnum */
memset( &rec, 0, sizeof rec );
rec.rectype = 0; /* unused record */
rec.recnum = recnum;
rc = 0;
if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
log_error(_("trustdb rec %lu: lseek failed: %s\n"),
recnum, strerror(errno) );
rc = G10ERR_WRITE_FILE;
}
else {
int n = write( db_fd, &rec, TRUST_RECORD_LEN);
if( n != TRUST_RECORD_LEN ) {
log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"),
recnum, n, strerror(errno) );
rc = G10ERR_WRITE_FILE;
}
}
if( rc )
log_fatal(_("%s: failed to append a record: %s\n"),
db_name, g10_errstr(rc));
}
return recnum ;
}
/****************
* Search the trustdb for a key which matches PK and return the dir record
* The local_id of PK is set to the correct value
*/
int
tdbio_search_dir_bypk( PKT_public_key *pk, TRUSTREC *rec )
{
byte fingerprint[MAX_FINGERPRINT_LEN];
size_t fingerlen;
u32 keyid[2];
int rc;
keyid_from_pk( pk, keyid );
fingerprint_from_pk( pk, fingerprint, &fingerlen );
rc = tdbio_search_dir_byfpr( fingerprint, fingerlen,
pk->pubkey_algo, rec );
if( !rc ) {
if( pk->local_id && pk->local_id != rec->recnum )
log_error("%s: found record, but LID from memory does "
"not match recnum (%lu,%lu)\n",
db_name, pk->local_id, rec->recnum );
pk->local_id = rec->recnum;
}
return rc;
}
static int
cmp_krec_fpr( void *dataptr, const TRUSTREC *rec )
{
const struct cmp_krec_fpr_struct *d = dataptr;
return rec->rectype == RECTYPE_KEY
&& ( !d->pubkey_algo || rec->r.key.pubkey_algo == d->pubkey_algo )
&& rec->r.key.fingerprint_len == d->fprlen
&& !memcmp( rec->r.key.fingerprint, d->fpr, d->fprlen );
}
int
tdbio_search_dir_byfpr( const byte *fingerprint, size_t fingerlen,
int pubkey_algo, TRUSTREC *rec )
{
struct cmp_krec_fpr_struct cmpdata;
ulong recnum;
int rc;
assert( fingerlen == 20 || fingerlen == 16 );
/* locate the key using the hash table */
cmpdata.pubkey_algo = pubkey_algo;
cmpdata.fpr = fingerprint;
cmpdata.fprlen = fingerlen;
rc = lookup_hashtable( get_keyhashrec(), fingerprint, fingerlen,
cmp_krec_fpr, &cmpdata, rec );
if( !rc ) {
recnum = rec->r.key.lid;
/* Now read the dir record */
rc = tdbio_read_record( recnum, rec, RECTYPE_DIR);
if( rc )
log_error("%s: can't read dirrec %lu: %s\n",
db_name, recnum, g10_errstr(rc) );
}
return rc;
}
static int
cmp_sdir( void *dataptr, const TRUSTREC *rec )
{
const struct cmp_sdir_struct *d = dataptr;
return rec->rectype == RECTYPE_SDIR
&& ( !d->pubkey_algo || rec->r.sdir.pubkey_algo == d->pubkey_algo )
&& rec->r.sdir.keyid[0] == d->keyid[0]
&& rec->r.sdir.keyid[1] == d->keyid[1];
}
int
tdbio_search_sdir( u32 *keyid, int pubkey_algo, TRUSTREC *rec )
{
struct cmp_sdir_struct cmpdata;
int rc;
byte key[8];
/* locate the shadow dir record using the hash table */
u32tobuf( key , keyid[0] );
u32tobuf( key+4 , keyid[1] );
cmpdata.pubkey_algo = pubkey_algo;
cmpdata.keyid[0] = keyid[0];
cmpdata.keyid[1] = keyid[1];
rc = lookup_hashtable( get_sdirhashrec(), key, 8,
cmp_sdir, &cmpdata, rec );
return rc;
}
diff --git a/g10/trustdb.c b/g10/trustdb.c
index 7a6ec3bd1..43903a996 100644
--- a/g10/trustdb.c
+++ b/g10/trustdb.c
@@ -1,2651 +1,2763 @@
/* trustdb.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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#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"
#if MAX_FINGERPRINT_LEN > 20
#error Must change structure of trustdb
#endif
-typedef struct local_id_info *LOCAL_ID_INFO;
-struct local_id_info {
- LOCAL_ID_INFO next;
+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;
+
typedef struct trust_info TRUST_INFO;
struct trust_info {
ulong lid;
- unsigned trust;
+ byte otrust; /* ownertrust (assigned trust) */
+ byte trust; /* calculated trust (validity) */
};
-
typedef struct trust_seg_list *TRUST_SEG_LIST;
struct trust_seg_list {
TRUST_SEG_LIST next;
- int nseg; /* number of segments */
- int dup;
- TRUST_INFO seg[1]; /* segment list */
+ int pathlen;
+ TRUST_INFO path[1];
};
struct recno_list_struct {
struct recno_list_struct *next;
ulong recno;
int type;
};
typedef struct recno_list_struct *RECNO_LIST;
static int walk_sigrecs( SIGREC_CONTEXT *c );
-static LOCAL_ID_INFO *new_lid_table(void);
-static void release_lid_table( LOCAL_ID_INFO *tbl );
-static int ins_lid_table_item( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag );
-static int qry_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned *flag );
+static LOCAL_ID_TABLE new_lid_table(void);
+static void release_lid_table( LOCAL_ID_TABLE tbl );
+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 void print_user_id( const char *text, u32 *keyid );
static int list_sigs( ulong pubkey_id );
static int do_check( TRUSTREC *drec, unsigned *trustlevel );
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 */
-static LOCAL_ID_INFO *ultikey_table;
+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;
#define HEXTOBIN(a) ( (a) >= '0' && (a) <= '9' ? ((a)-'0') : \
(a) >= 'A' && (a) <= 'F' ? ((a)-'A'+10) : ((a)-'a'+10))
/**********************************************
*********** record read write **************
**********************************************/
static void
die_invalid_db()
{
log_error(_(
"The trust DB is corrupted; please run \"gpgm --fix-trust-db\".\n") );
g10_exit(2);
}
/****************
* 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) );
die_invalid_db();
}
/****************
* 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) );
die_invalid_db();
}
/****************
* 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) );
die_invalid_db();
}
/****************
* sync the db
*/
static void
do_sync( )
{
int rc = tdbio_sync();
if( !rc )
return;
log_error("trust db: sync failed: %s\n", g10_errstr(rc) );
g10_exit(2);
}
/**********************************************
************* list helpers *******************
**********************************************/
/****************
* Insert a new item into a recno list
*/
static void
ins_recno_list( RECNO_LIST *head, ulong recno, int type )
{
RECNO_LIST item = m_alloc( sizeof *item );
item->recno = recno;
item->type = type;
item->next = *head;
*head = item;
}
static RECNO_LIST
qry_recno_list( RECNO_LIST list, ulong recno, int type )
{
for( ; list; list = list->next ) {
if( list->recno == recno && (!type || list->type == type) )
return list;
}
return NULL;
}
static void
rel_recno_list( RECNO_LIST *head )
{
RECNO_LIST r, r2;
for(r = *head; r; r = r2 ) {
r2 = r->next;
m_free(r);
}
*head = NULL;
}
-static LOCAL_ID_INFO *
+static LOCAL_ID_TABLE
new_lid_table(void)
{
- return m_alloc_clear( 16 * sizeof(LOCAL_ID_INFO));
+ LOCAL_ID_TABLE a;
+
+ a = unused_lid_tables;
+ if( a ) {
+ unused_lid_tables = a->next;
+ a->next = NULL;
+ }
+ else
+ a = m_alloc_clear( sizeof *a );
+ return a;
}
static void
-release_lid_table( LOCAL_ID_INFO *tbl )
+release_lid_table( LOCAL_ID_TABLE tbl )
{
- LOCAL_ID_INFO a, a2;
+ struct local_id_item *a, *a2;
int i;
for(i=0; i < 16; i++ ) {
- for(a=tbl[i]; a; a = a2 ) {
+ for(a=tbl->items[i]; a; a = a2 ) {
a2 = a->next;
- m_free(a);
+ a->next = unused_lid_items;
+ unused_lid_items = a;
}
}
- m_free(tbl);
+ tbl->next = unused_lid_tables;
+ unused_lid_tables = tbl;
}
/****************
* Add a new item to the table or return 1 if we already have this item
- * fixme: maybe it's a good idea to take items from an unused item list.
*/
static int
-ins_lid_table_item( LOCAL_ID_INFO *tbl, ulong lid, unsigned flag )
+ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag )
{
- LOCAL_ID_INFO a;
+ struct local_id_item *a;
- for( a = tbl[lid & 0x0f]; a; a = a->next )
+ for( a = tbl->items[lid & 0x0f]; a; a = a->next )
if( a->lid == lid )
return 1;
- a = m_alloc( sizeof *a );
+ 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[lid & 0x0f];
- tbl[lid & 0x0f] = a;
+ a->next = tbl->items[lid & 0x0f];
+ tbl->items[lid & 0x0f] = a;
return 0;
}
static int
-qry_lid_table_flag( LOCAL_ID_INFO *tbl, ulong lid, unsigned *flag )
+qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag )
{
- LOCAL_ID_INFO a;
+ struct local_id_item *a;
- for( a = tbl[lid & 0x0f]; a; a = a->next )
+ for( a = tbl->items[lid & 0x0f]; a; a = a->next )
if( a->lid == lid ) {
if( flag )
*flag = a->flag;
return 0;
}
return -1;
}
/****************
* Return the keyid from the primary key identified by LID.
*/
int
keyid_from_lid( ulong lid, u32 *keyid )
{
TRUSTREC rec;
int rc;
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;
get_dir_record( pk, &rec );
}
return pk->local_id;
}
/****************
* 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
*/
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;
die_invalid_db();
}
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;
}
/***********************************************
************* Trust stuff ******************
***********************************************/
/****************
* Verify that all our public keys are in the trustDB.
*/
static int
verify_own_keys()
{
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 ) ) ) {
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;
}
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 );
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 secret key table\n"),
(ulong)keyid[1]);
else if( opt.verbose )
log_info(_("key %08lX: accepted as secret key.\n"),
(ulong)keyid[1]);
skip:
release_secret_key_parts( sk );
release_public_key_parts( pk );
}
if( rc != -1 )
log_error(_("enum_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;
}
static void
print_user_id( const char *text, u32 *keyid )
{
char *p;
size_t n;
p = get_user_id( keyid, &n );
if( *text ) {
fputs( text, stdout);
putchar(' ');
}
putchar('\"');
print_string( stdout, p, n, 0 );
putchar('\"');
putchar('\n');
m_free(p);
}
-
+#if 0
static int
print_keyid( FILE *fp, ulong lid )
{
u32 ki[2];
if( keyid_from_lid( lid, ki ) )
return fprintf(fp, "????????.%lu", lid );
else
return fprintf(fp, "%08lX.%lu", (ulong)ki[1], lid );
}
static int
print_trust( FILE *fp, unsigned trust )
{
int c;
switch( trust ) {
case TRUST_UNKNOWN: c = 'o'; break;
case TRUST_EXPIRED: c = 'e'; break;
case TRUST_UNDEFINED: c = 'q'; break;
case TRUST_NEVER: c = 'n'; break;
case TRUST_MARGINAL: c = 'm'; break;
case TRUST_FULLY: c = 'f'; break;
case TRUST_ULTIMATE: c = 'u'; break;
default: fprintf(fp, "%02x", trust ); return 2;
}
putc(c, fp);
return 1;
}
-
+#endif
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;
}
/* (a non-recursive algorithm would be easier) */
static int
do_list_sigs( ulong root, ulong pk_lid, int depth,
- LOCAL_ID_INFO *lids, unsigned *lineno )
+ 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 );
- if( rc )
+ rc = walk_sigrecs( &sx ); /* should we replace it and use */
+ if( rc ) /* use a loop like in collect_paths ??*/
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;
}
/****************
* List all signatures of a public key
*/
static int
list_sigs( ulong pubkey_id )
{
int rc;
u32 keyid[2];
- LOCAL_ID_INFO *lids;
+ 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);
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;
}
+
/****************
- * 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.
+ * stack is an array of (max_path+1) elements. If trust_seg_head is not
+ * NULL it is a pointer to a variable which will receive a linked list
+ * of trust paths - The caller has to free the memory.
*/
static int
-verify_key( int depth, int max_depth, TRUSTREC *drec )
+collect_paths( int depth, int max_depth, int all, TRUSTREC *drec,
+ TRUST_INFO *stack, TRUST_SEG_LIST *trust_seg_head )
{
ulong rn, uidrn;
int marginal=0;
int fully=0;
- size_t dbglen;
-
- /* Note: If used stack size is an issue, we could reuse the
- * trustrec vars and reread them as needed */
+ LOCAL_ID_TABLE sigs_seen = NULL;
- dbglen = printf("verify_key: depth=%d %*s", depth, depth*3,"" );
- dbglen += print_keyid( stdout, drec->recnum );
- dbglen += printf(" ot=");
- dbglen += print_trust(stdout, drec->r.dir.ownertrust );
- dbglen += printf(" -> ");
-
- if( depth >= max_depth ) {
- /* max cert_depth reached */
- puts("undefined (too deep)");
+ if( depth >= max_depth ) /* max cert_depth reached */
return TRUST_UNDEFINED;
+ { int i;
+
+ for(i=0; i < depth; i++ )
+ if( stack[i].lid == drec->r.dir.lid )
+ return TRUST_UNDEFINED; /* closed (we already visited this lid) */
}
+
+ stack[depth].lid = drec->r.dir.lid;
+ stack[depth].otrust = drec->r.dir.ownertrust;
+ stack[depth].trust = 0;
if( !qry_lid_table_flag( ultikey_table, drec->r.dir.lid, NULL ) ) {
/* we are at the end of a path */
- puts("ultimate");
+ TRUST_SEG_LIST tsl;
+ int i;
+
+ stack[depth].trust = TRUST_ULTIMATE;
+ if( trust_seg_head ) {
+ /* we can now put copy our current stack to the trust_seg_list */
+ tsl = m_alloc( sizeof *tsl + (depth+1)*sizeof( TRUST_INFO ) );
+ for(i=0; i <= depth; i++ )
+ tsl->path[i] = stack[i];
+ tsl->pathlen = i;
+ tsl->next = *trust_seg_head;
+ *trust_seg_head = tsl;
+ }
return TRUST_ULTIMATE;
}
/* loop over all user-ids */
+ if( !all )
+ sigs_seen = new_lid_table();
for( rn = drec->r.dir.uidlist; rn; rn = uidrn ) {
TRUSTREC rec; /* used for uids and sigs */
ulong sigrn;
read_record( rn, &rec, RECTYPE_UID );
uidrn = rec.r.uid.next;
/* fixme: continue if the uidrec is not marked valid */
/* loop over all signature records */
for( rn = rec.r.uid.siglist; rn; rn = sigrn ) {
int i;
read_record( rn, &rec, RECTYPE_SIG );
sigrn = rec.r.sig.next;
for(i=0; i < SIGS_PER_RECORD; i++ ) {
TRUSTREC tmp;
int ot, nt;
if( !rec.r.sig.sig[i].lid )
continue; /* skip deleted sigs */
if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) )
continue; /* skip unchecked signatures */
if( !(rec.r.sig.sig[i].flag & SIGF_VALID) )
continue; /* skip invalid signatures */
if( (rec.r.sig.sig[i].flag & SIGF_EXPIRED) )
continue; /* skip expired signatures */
if( (rec.r.sig.sig[i].flag & SIGF_REVOKED) )
continue; /* skip revoked signatures */
- /* fixme: skip duplicates */
- read_record( rec.r.sig.sig[i].lid, &tmp, RECTYPE_DIR );
- ot = tmp.r.dir.ownertrust & TRUST_MASK;
- #if 0 /* Does not work, because the owner trust of our
- * own keys is not always set
- * -- fix this in verify_own_keys() ? */
- if( ot < TRUST_MARGINAL ) {
- printf(". ");
- continue; /* ownertrust is too low; don't need to check */
+ /* visit every signer only once (a signer may have
+ * signed multizple user IDs */
+ if( sigs_seen && ins_lid_table_item( sigs_seen,
+ rec.r.sig.sig[i].lid, 0) )
+ continue; /* we alread have this one */
+
+ read_record( rec.r.sig.sig[i].lid, &tmp, 0 );
+ if( tmp.rectype != RECTYPE_DIR ) {
+ log_info("oops: lid %lu: sig %lu has rectype %d"
+ " - skipped\n",
+ drec->r.dir.lid, tmp.recnum, tmp.rectype );
+ continue;
}
- #endif
+ ot = tmp.r.dir.ownertrust & TRUST_MASK;
if( ot >= TRUST_FULLY )
ot = TRUST_FULLY; /* just in case */
+ nt = collect_paths( depth+1, max_depth, all, &tmp, stack,
+ trust_seg_head );
+ nt &= TRUST_MASK;
- puts("");
- nt = verify_key( depth+1, max_depth, &tmp ) & TRUST_MASK;
if( nt < TRUST_MARGINAL ) {
- printf("%*s* ", dbglen, "");
- dbglen += 2;
continue;
}
if( nt == TRUST_ULTIMATE ) {
/* we have signed this key and only in this special case
- * we assume a completes-needed or marginals-needed of 1 */
- printf("%*s", dbglen, "");
- if( ot == TRUST_MARGINAL )
- puts("marginal (1st level)");
- else if( ot == TRUST_FULLY )
- puts("fully (1st level)");
- else
- puts("????? (1st level)");
- return ot;
+ * we assume that this one is fully trusted */
+ if( !all ) {
+ if( sigs_seen )
+ release_lid_table( sigs_seen );
+ return (stack[depth].trust = TRUST_FULLY);
+ }
}
if( nt >= TRUST_FULLY )
fully++;
if( nt >= TRUST_MARGINAL )
marginal++;
if( fully >= opt.completes_needed
|| marginal >= opt.marginals_needed ) {
- printf("%*s", dbglen, "");
- puts("fully");
- return TRUST_FULLY;
+ if( !all ) {
+ if( sigs_seen )
+ release_lid_table( sigs_seen );
+ return (stack[depth].trust = TRUST_FULLY);
+ }
}
}
}
}
- printf("%*s", dbglen, "");
+ if( sigs_seen )
+ release_lid_table( sigs_seen );
+ if( all && ( fully >= opt.completes_needed
+ || marginal >= opt.marginals_needed ) ) {
+ return (stack[depth].trust = TRUST_FULLY );
+ }
if( marginal ) {
- puts("marginal");
- return TRUST_MARGINAL;
+ return (stack[depth].trust = TRUST_MARGINAL);
}
- puts("undefined");
- return TRUST_UNDEFINED;
+ return (stack[depth].trust=TRUST_UNDEFINED);
}
+/****************
+ * 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
-list_paths( int depth, int max_depth, TRUSTREC *drec )
+verify_key( int max_depth, TRUSTREC *drec )
{
- ulong rn, uidrn;
- int marginal=0;
- int fully=0;
- size_t dbglen;
+ TRUST_INFO *tmppath = m_alloc_clear( (max_depth+1)* sizeof *tmppath );
+ int tr;
- if( depth >= max_depth ) {
- /* max cert_depth reached */
- puts("undefined (too deep)");
- return TRUST_UNDEFINED;
- }
- if( !qry_lid_table_flag( ultikey_table, drec->r.dir.lid, NULL ) ) {
- /* we are at the end of a path */
- puts("ultimate");
- return TRUST_ULTIMATE;
- }
-
- /* loop over all user-ids */
- for( rn = drec->r.dir.uidlist; rn; rn = uidrn ) {
- TRUSTREC rec; /* used for uids and sigs */
- ulong sigrn;
-
- read_record( rn, &rec, RECTYPE_UID );
- uidrn = rec.r.uid.next;
- /* fixme: continue if the uidrec is not marked valid */
-
- /* loop over all signature records */
- for( rn = rec.r.uid.siglist; rn; rn = sigrn ) {
- int i;
-
- read_record( rn, &rec, RECTYPE_SIG );
- sigrn = rec.r.sig.next;
-
- for(i=0; i < SIGS_PER_RECORD; i++ ) {
- TRUSTREC tmp;
- int ot, nt;
-
- if( !rec.r.sig.sig[i].lid )
- continue; /* skip deleted sigs */
- if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) )
- continue; /* skip unchecked signatures */
- if( !(rec.r.sig.sig[i].flag & SIGF_VALID) )
- continue; /* skip invalid signatures */
- if( (rec.r.sig.sig[i].flag & SIGF_EXPIRED) )
- continue; /* skip expired signatures */
- if( (rec.r.sig.sig[i].flag & SIGF_REVOKED) )
- continue; /* skip revoked signatures */
- /* fixme: skip duplicates */
-
- read_record( rec.r.sig.sig[i].lid, &tmp, RECTYPE_DIR );
- ot = tmp.r.dir.ownertrust & TRUST_MASK;
- if( ot < TRUST_MARGINAL ) {
- printf(". ");
- continue; /* ownertrust is too low; don't need to check */
- }
-
- if( ot >= TRUST_FULLY )
- ot = TRUST_FULLY; /* just in case */
-
- puts("");
- nt = verify_key( depth+1, max_depth, &tmp ) & TRUST_MASK;
- if( nt < TRUST_MARGINAL ) {
- printf("%*s* ", dbglen, "");
- dbglen += 2;
- continue;
- }
-
- if( nt == TRUST_ULTIMATE ) {
- /* we have signed this key and only in this special case
- * we assume a completes-needed or marginals-needed of 1 */
- printf("%*s", dbglen, "");
- if( ot == TRUST_MARGINAL )
- puts("marginal (1st level)");
- else if( ot == TRUST_FULLY )
- puts("fully (1st level)");
- else
- puts("????? (1st level)");
- return ot;
- }
+ tr = collect_paths( 0, max_depth, 0, drec, tmppath, NULL );
+ m_free( tmppath );
+ return tr;
+}
- if( nt >= TRUST_FULLY )
- fully++;
- if( nt >= TRUST_MARGINAL )
- marginal++;
- if( fully >= opt.completes_needed
- || marginal >= opt.marginals_needed ) {
- printf("%*s", dbglen, "");
- puts("fully");
- return TRUST_FULLY;
- }
- }
- }
- }
- printf("%*s", dbglen, "");
- if( marginal ) {
- puts("marginal");
- return TRUST_MARGINAL;
- }
- puts("undefined");
- return TRUST_UNDEFINED;
-}
/****************
* 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 *trustlevel )
{
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;
}
- *trustlevel = verify_key( 1, 5, dr );
+ *trustlevel = verify_key( 5, dr );
if( dr->r.dir.dirflags & DIRF_REVOKED )
*trustlevel |= TRUST_FLAG_REVOKED;
return 0;
}
/****************
* Perform some checks over the trustdb
* level 0: only open the db
* 1: used for initial program startup
*/
int
init_trustdb( int level, const char *dbname )
{
int rc=0;
if( !ultikey_table )
ultikey_table = new_lid_table();
if( !level || level==1 ) {
rc = tdbio_set_dbname( dbname, !!level );
if( rc )
return rc;
if( !level )
return 0;
/* 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();
return rc;
}
void
list_trustdb( const char *username )
{
TRUSTREC rec;
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( pk, username )) )
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;
printf("# List of assigned trustvalues, created %s\n"
"# (Use \"gpgm --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("Oops; 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;
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 to 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 otrust 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 );
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();
}
+
+
+static void
+print_path( int pathlen, TRUST_INFO *path )
+{
+ int rc, i;
+ u32 keyid[2];
+
+ fputs("path:", stdout);
+ for( i = 0; i < pathlen; i++ ) {
+ if( i && !(i%4) )
+ fputs(" ", stdout );
+ rc = keyid_from_lid( path[i].lid, keyid );
+ if( rc )
+ printf(" ????????.%lu:", path[i].lid );
+ else
+ printf(" %08lX.%lu:", (ulong)keyid[1], path[i].lid );
+ print_sigflags( stdout, path[i].otrust );
+ }
+ putchar('\n');
+}
+
+
+
void
list_trust_path( int max_depth, const char *username )
{
int rc;
int wipe=0;
TRUSTREC rec;
+ TRUST_INFO *tmppath;
+ TRUST_SEG_LIST trust_seg_list, tsl, tsl2;
PKT_public_key *pk = m_alloc_clear( sizeof *pk );
if( max_depth < 0 ) {
wipe = 1;
max_depth = -max_depth;
}
- if( max_depth < 1 )
- max_depth = 1;
if( (rc = get_pubkey_byname( pk, username )) )
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 );
if( rc )
log_error("failed to put '%s' into trustdb: %s\n", username, g10_errstr(rc));
else {
assert( pk->local_id );
}
}
+ free_public_key( pk );
+ /* collect the paths */
+ tmppath = m_alloc_clear( (max_depth+1)* sizeof *tmppath );
+ trust_seg_list = NULL;
+ collect_paths( 0, max_depth, 1, &rec, tmppath, &trust_seg_list );
+ m_free( tmppath );
+ /* and now print them */
+ for(tsl = trust_seg_list; tsl; tsl = tsl->next ) {
+ print_path( tsl->pathlen, tsl->path );
+ }
- free_public_key( pk );
+ /* release the list */
+ for(tsl = trust_seg_list; tsl; tsl = tsl2 ) {
+ tsl2 = tsl->next;
+ m_free( tsl );
+ }
+ trust_seg_list = NULL;
}
/****************
* Check the complete trustdb or only the entries for the given username.
+ * We check the complete database and recalculate all flags.
*/
void
check_trustdb( const char *username )
{
TRUSTREC rec;
KBNODE keyblock = NULL;
KBPOS kbpos;
int rc;
if( username ) {
rc = find_keyblock_byname( &kbpos, username );
if( !rc )
rc = read_keyblock( &kbpos, &keyblock );
if( rc ) {
log_error("%s: keyblock read problem: %s\n",
username, g10_errstr(rc));
}
else {
int modified;
- rc = update_trust_record( keyblock, &modified );
+ rc = update_trust_record( keyblock, 0, &modified );
if( rc == -1 ) { /* not yet in trustdb: insert */
rc = insert_trust_record(
find_kbnode( keyblock, PKT_PUBLIC_KEY
) ->pkt->pkt.public_key );
}
if( rc )
log_error("%s: update failed: %s\n",
username, g10_errstr(rc) );
else if( modified )
log_info("%s: updated\n", username );
else
log_info("%s: okay\n", username );
}
release_kbnode( keyblock ); keyblock = NULL;
}
else {
ulong recnum;
ulong count=0, upd_count=0, err_count=0, skip_count=0;
for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) {
if( rec.rectype == RECTYPE_DIR ) {
TRUSTREC tmp;
int modified;
if( !rec.r.dir.keylist ) {
log_info("lid %lu: dir record w/o key - skipped\n", recnum);
count++;
skip_count++;
continue;
}
read_record( rec.r.dir.keylist, &tmp, RECTYPE_KEY );
rc = get_keyblock_byfprint( &keyblock,
tmp.r.key.fingerprint,
tmp.r.key.fingerprint_len );
if( rc ) {
log_error("lid %lu: keyblock not found: %s\n",
recnum, g10_errstr(rc) );
count++;
skip_count++;
continue;
}
- rc = update_trust_record( keyblock, &modified );
+ rc = update_trust_record( keyblock, 0, &modified );
if( rc ) {
log_error("lid %lu: update failed: %s\n",
recnum, g10_errstr(rc) );
err_count++;
}
else if( modified ) {
if( opt.verbose )
log_info("lid %lu: updated\n", recnum );
upd_count++;
}
else if( opt.verbose > 1 )
log_info("lid %lu: okay\n", recnum );
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( 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);
}
}
+/****************
+ * Put new entries from the pubrings into the trustdb.
+ * This function honors the sig flags to speed up the check.
+ */
void
update_trustdb( )
{
KBNODE keyblock = NULL;
KBPOS kbpos;
int rc;
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, &modified );
+ rc = update_trust_record( keyblock, 1, &modified );
if( rc == -1 ) { /* not yet in trustdb: insert */
PKT_public_key *pk =
find_kbnode( keyblock, PKT_PUBLIC_KEY
) ->pkt->pkt.public_key;
rc = insert_trust_record( pk );
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("enum_keyblocks failed: %s\n", g10_errstr(rc));
enum_keyblocks( 2, &kbpos, &keyblock ); /* close */
release_kbnode( keyblock );
}
/****************
* 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 )
{
TRUSTREC rec;
unsigned trustlevel = TRUST_UNKNOWN;
int rc=0;
u32 cur_time;
u32 keyid[2];
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 );
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;
}
else {
rc = do_check( &rec, &trustlevel );
if( rc ) {
log_error(_("key %08lX.%lu: trust check failed: %s\n"),
(ulong)keyid[1], pk->local_id, g10_errstr(rc));
return rc;
}
}
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 )
{
unsigned trustlevel;
int c;
if( check_trust( pk, &trustlevel ) )
return '?';
if( trustlevel & TRUST_FLAG_REVOKED )
return 'r';
switch( (trustlevel & TRUST_MASK) ) {
case TRUST_UNKNOWN: c = 'o'; break;
case TRUST_EXPIRED: c = 'e'; break;
case TRUST_UNDEFINED: c = 'q'; break;
case TRUST_NEVER: c = 'n'; break;
case TRUST_MARGINAL: c = 'm'; break;
case TRUST_FULLY: c = 'f'; break;
case TRUST_ULTIMATE: c = 'u'; break;
default: BUG();
}
return c;
}
/****************
* 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.
*
* 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.
*/
int
enum_trust_web( void **context, ulong *lid )
{
/* REPLACE THIS with a BETTER ONE */
return -1; /* eof */
}
/****************
* Return the assigned ownertrust value for the given LID
*/
unsigned
get_ownertrust( ulong lid )
{
TRUSTREC rec;
read_record( lid, &rec, RECTYPE_DIR );
return rec.r.dir.ownertrust;
}
int
get_ownertrust_info( ulong lid )
{
unsigned otrust;
int c;
otrust = get_ownertrust( lid );
switch( (otrust & TRUST_MASK) ) {
case TRUST_NEVER: c = 'n'; break;
case TRUST_MARGINAL: c = 'm'; break;
case TRUST_FULLY: c = 'f'; break;
case TRUST_ULTIMATE: c = 'u'; break;
default: c = '-'; break;
}
return c;
}
byte *
get_pref_data( ulong lid, const byte *namehash, size_t *ret_n )
{
TRUSTREC rec;
ulong recno;
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;
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;
}
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;
}
/****************
* This function simply looks for the key in the trustdb
* and makes sure that pk->local_id is set to the coreect value.
* Return: 0 = found
* -1 = not found
* other = error
*/
int
query_trust_record( PKT_public_key *pk )
{
TRUSTREC rec;
return get_dir_record( pk, &rec );
}
/* FIXME: Brauchen wir das?? */
int
clear_trust_checked_flag( PKT_public_key *pk )
{
TRUSTREC rec;
int rc;
rc = get_dir_record( pk, &rec );
if( rc )
return rc;
if( !(rec.r.dir.dirflags & DIRF_CHECKED) )
return 0;
/* reset the flag */
rec.r.dir.dirflags &= ~DIRF_CHECKED;
write_record( &rec );
do_sync();
return 0;
}
static void
check_hint_sig( ulong lid, KBNODE keyblock, u32 *keyid, byte *uidrec_hash,
TRUSTREC *sigrec, int sigidx, ulong hint_owner )
{
KBNODE node;
int rc, state;
byte uhash[20];
int is_selfsig;
PKT_signature *sigpkt = NULL;
TRUSTREC tmp;
u32 sigkid[2];
if( sigrec->r.sig.sig[sigidx].flag & SIGF_CHECKED )
log_info(_("note: sig rec %lu[%d] in hintlist "
"of %lu but marked as checked\n"),
sigrec->recnum, sigidx, hint_owner );
if( !(sigrec->r.sig.sig[sigidx].flag & SIGF_NOPUBKEY) )
log_info(_("note: sig rec %lu[%d] in hintlist "
"of %lu but not marked\n"),
sigrec->recnum, sigidx, hint_owner );
read_record( sigrec->r.sig.sig[sigidx].lid, &tmp, 0 );
if( tmp.rectype != RECTYPE_DIR ) {
/* we need the dir record */
log_error(_("sig rec %lu[%d] in hintlist "
"of %u does not point to a dir record\n"),
sigrec->recnum, sigidx, hint_owner );
return;
}
if( !tmp.r.dir.keylist ) {
log_error(_("lid %lu: no primary key\n"), tmp.r.dir.lid );
return;
}
read_record(tmp.r.dir.keylist, &tmp, RECTYPE_KEY );
keyid_from_fingerprint( tmp.r.key.fingerprint,
tmp.r.key.fingerprint_len, sigkid );
/* find the correct signature packet */
state = 0;
for( node=keyblock; node; node = node->next ) {
if( node->pkt->pkttype == PKT_USER_ID ) {
PKT_user_id *uidpkt = node->pkt->pkt.user_id;
if( state )
break;
rmd160_hash_buffer( uhash, uidpkt->name, uidpkt->len );
if( !memcmp( uhash, uidrec_hash, 20 ) )
state = 1;
}
else if( state && node->pkt->pkttype == PKT_SIGNATURE ) {
sigpkt = node->pkt->pkt.signature;
if( sigpkt->keyid[0] == sigkid[0]
&& sigpkt->keyid[1] == sigkid[1]
&& (sigpkt->sig_class&~3) == 0x10 ) {
state = 2;
break; /* found */
}
}
}
if( !node ) {
log_info(_("lid %lu: user id not found in keyblock\n"), lid );
return ;
}
if( state != 2 ) {
log_info(_("lid %lu: user id without signature\n"), lid );
return ;
}
/* and check the sig */
rc = check_key_signature( keyblock, node, &is_selfsig );
if( is_selfsig ) {
log_error(_("lid %lu: self-signature in hintlist\n"), lid );
return;
}
if( !rc ) { /* valid signature */
if( opt.verbose )
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
"Good signature (3)\n"),
(ulong)keyid[1], lid, uhash[18], uhash[19],
(ulong)sigpkt->keyid[1] );
sigrec->r.sig.sig[sigidx].flag = SIGF_CHECKED | SIGF_VALID;
}
else if( rc == G10ERR_NO_PUBKEY ) {
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
"very strange: no public key\n"),
(ulong)keyid[1], lid, uhash[18], uhash[19],
(ulong)sigpkt->keyid[1] );
sigrec->r.sig.sig[sigidx].flag = SIGF_NOPUBKEY;
}
else {
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: %s\n"),
(ulong)keyid[1], lid, uhash[18], uhash[19],
(ulong)sigpkt->keyid[1], g10_errstr(rc) );
sigrec->r.sig.sig[sigidx].flag = SIGF_CHECKED;
}
sigrec->dirty = 1;
}
/****************
* Process a hintlist.
* Fixme: this list is not anymore anchored to another
* record, so it should be put elsewehere in case of an error
*/
static void
process_hintlist( ulong hintlist, ulong hint_owner )
{
ulong hlst_rn;
int rc;
for( hlst_rn = hintlist; hlst_rn; ) {
TRUSTREC hlstrec;
int hlst_idx;
read_record( hlst_rn, &hlstrec, RECTYPE_HLST );
for( hlst_idx=0; hlst_idx < ITEMS_PER_HLST_RECORD; hlst_idx++ ) {
TRUSTREC dirrec;
TRUSTREC uidrec;
TRUSTREC tmprec;
KBNODE keyblock = NULL;
u32 keyid[2];
ulong lid;
ulong r1, r2;
lid = hlstrec.r.hlst.rnum[hlst_idx];
if( !lid )
continue;
read_record( lid, &dirrec, 0 );
/* make sure it points to a dir record:
* this should be true because it only makes sense to
* call this function if the dir record is available */
if( dirrec.rectype != RECTYPE_DIR ) {
log_error(_("hintlist %lu[%d] of %lu "
"does not point to a dir record\n"),
hlst_rn, hlst_idx, hint_owner );
continue;
}
if( !dirrec.r.dir.keylist ) {
log_error(_("lid %lu does not have a key\n"), lid );
continue;
}
/* get the keyblock */
read_record( dirrec.r.dir.keylist, &tmprec, RECTYPE_KEY );
rc = get_keyblock_byfprint( &keyblock,
tmprec.r.key.fingerprint,
tmprec.r.key.fingerprint_len );
if( rc ) {
log_error(_("lid %lu: can't get keyblock: %s\n"),
lid, g10_errstr(rc) );
continue;
}
keyid_from_fingerprint( tmprec.r.key.fingerprint,
tmprec.r.key.fingerprint_len, keyid );
/* Walk over all user ids and their signatures and check all
* the signature which are created by hint_owner */
for( r1 = dirrec.r.dir.uidlist; r1; r1 = uidrec.r.uid.next ) {
TRUSTREC sigrec;
read_record( r1, &uidrec, RECTYPE_UID );
for( r2 = uidrec.r.uid.siglist; r2; r2 = sigrec.r.sig.next ) {
int i;
read_record( r2, &sigrec, RECTYPE_SIG );
sigrec.dirty = 0;
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].lid != hint_owner )
continue; /* not for us */
/* some diagnostic messages */
/* and do the signature check */
check_hint_sig( lid, keyblock, keyid,
uidrec.r.uid.namehash,
&sigrec, i, hint_owner );
}
if( sigrec.dirty )
write_record( &sigrec );
}
}
release_kbnode( keyblock );
} /* loop over hlst entries */
/* delete this hlst record */
hlst_rn = hlstrec.r.hlst.next;
delete_record( hlstrec.recnum );
} /* loop over hintlist */
}
/****************
* Create or update shadow dir record and return the LID of the record
*/
static ulong
create_shadow_dir( PKT_signature *sig, ulong lid )
{
TRUSTREC sdir, hlst, tmphlst;
ulong recno, newlid;
int tmpidx;
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_dir failed: %s\n", g10_errstr(rc));
die_invalid_db();
}
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;
sdir.r.sdir.hintlist = 0;
write_record( &sdir );
}
newlid = sdir.recnum;
/* Put the record number into the hintlist.
* (It is easier to use the lid and not the record number of the
* key to save some space (assuming that a signator has
* signed more than one user id - and it is easier to implement.)
*/
tmphlst.recnum = 0;
for( recno=sdir.r.sdir.hintlist; recno; recno = hlst.r.hlst.next) {
int i;
read_record( recno, &hlst, RECTYPE_HLST );
for( i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
if( !hlst.r.hlst.rnum[i] ) {
if( !tmphlst.recnum ) {
tmphlst = hlst;
tmpidx = i;
}
}
else if( hlst.r.hlst.rnum[i] == lid )
return newlid; /* the signature is already in the hintlist */
}
}
/* not yet in the hint list, write it */
if( tmphlst.recnum ) { /* we have an empty slot */
tmphlst.r.hlst.rnum[tmpidx] = lid;
write_record( &tmphlst );
}
else { /* must append a new hlst record */
memset( &hlst, 0, sizeof hlst );
hlst.recnum = tdbio_new_recnum();
hlst.rectype = RECTYPE_HLST;
hlst.r.hlst.next = sdir.r.sdir.hintlist;
hlst.r.hlst.rnum[0] = lid;
write_record( &hlst );
sdir.r.sdir.hintlist = hlst.recnum;
write_record( &sdir );
}
return newlid;
}
+/****************
+ * This function checks the given public key and inserts or updates
+ * the keyrecord from the trustdb. Revocation certificates
+ * are handled here and the keybinding of subkeys is checked.
+ * Hmmm: Should we check here, that the key has at least one valid
+ * user ID or do we allow keys w/o user ID?
+ *
+ * keyblock points to the first node in the keyblock,
+ * keynode is the node with the public key to check
+ * (either primary or secondary), keyid is the keyid of
+ * the primary key, drec is the directory record and recno_list
+ * is a list used to keep track of visited records.
+ * Existing keyflags are recalculated if recheck is true.
+ */
static void
-upd_key_record( PKT_public_key *pk, TRUSTREC *drec, RECNO_LIST *recno_list )
+upd_key_record( KBNODE keyblock, KBNODE keynode, u32 *keyid,
+ TRUSTREC *drec, RECNO_LIST *recno_list, int recheck )
{
TRUSTREC krec;
+ KBNODE node;
+ PKT_public_key *pk = keynode->pkt->pkt.public_key,;
+ ulon lid = drec->recnum;
byte fpr[MAX_FINGERPRINT_LEN];
size_t fprlen;
ulong recno, newrecno;
+ int keybind_seen = 0;
+ int revoke_seen = 0;
+ int rc;
fingerprint_from_pk( pk, fpr, &fprlen );
/* do we already have this key? */
for( recno=drec->r.dir.keylist; recno; recno = krec.r.key.next ) {
read_record( recno, &krec, RECTYPE_KEY );
if( krec.r.key.fingerprint_len == fprlen
&& !memcmp( krec.r.key.fingerprint, fpr, fprlen ) )
break;
}
if( recno ) { /* yes */
ins_recno_list( recno_list, recno, RECTYPE_KEY );
}
else { /* no: insert this new key */
+ recheck = 1;
memset( &krec, 0, sizeof(krec) );
krec.rectype = RECTYPE_KEY;
- krec.r.key.lid = drec->recnum;
+ krec.r.key.lid = lid;
krec.r.key.pubkey_algo = pk->pubkey_algo;
krec.r.key.fingerprint_len = fprlen;
memcpy(krec.r.key.fingerprint, fpr, fprlen );
krec.recnum = newrecno = tdbio_new_recnum();
write_record( &krec );
ins_recno_list( recno_list, newrecno, RECTYPE_KEY );
/* and put this new record at the end of the keylist */
if( !(recno=drec->r.dir.keylist) ) {
/* this is the first key */
drec->r.dir.keylist = newrecno;
drec->dirty = 1;
}
- else { /* we already have a key, append it to the list */
+ else { /* we already have a key, append the new one */
+ TRUSTREC save = krec;
for( ; recno; recno = krec.r.key.next )
read_record( recno, &krec, RECTYPE_KEY );
krec.r.key.next = newrecno;
write_record( &krec );
+ krec = save;
}
}
+
+ if( !recheck && (krec.r.key.keyflags & KEYF_CHECKED) )
+ return;
+
+ /* check keybindings and revocations */
+ krec.r.key.keyflags = 0;
+ if( keynode->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ /* we assume that a primary key is always valid
+ * and check later whether we have a revocation */
+ krec.r.key.keyflags |= KEYF_CHECKED | KEYF_VALID;
+ }
+
+ for( node=keynode->next; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break; /* ready */
+ else if( node->pkt->pkttype == PKT_PUBLIC_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+
+ if( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] )
+ continue; /* not a self signature */
+ if( sig->sig_class == 0x18 && !keybind_seen ) { /* a keybinding */
+ if( keynode->pkt->pkttype == PKT_PUBLIC_KEY )
+ continue; /* oops, not for a main key */
+ /* we 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 );
+ krec.r.key.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) );
+ krec.r.key.keyflags |= KEYF_CHECKED;
+ krec.r.key.keyflags &= ~KEYF_VALID;
+ }
+ keybind_seen = 1;
+ }
+ else if( sig->sig_class == 0x20 && !revoke_seen ) {
+ if( keynode->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ continue; /* a subkey is not expected here */
+ /* 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 );
+ krec.r.key.keyflags |= KEYF_REVOKED;
+ }
+ 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 ) {
+ if( keynode->pkt->pkttype == PKT_PUBLIC_KEY )
+ continue; /* a mainkey is not expected here */
+ /* This is a subkey revocation certificate: check it */
+ /* fixme: we should also check the revocation
+ * is newer tha 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 );
+ krec.r.key.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;
+ }
+ } /* end signature */
+ }
+
+ write_record( &krec );
}
+/****************
+ * This function checks the given user ID and inserts or updates
+ * the uid record of the trustdb. Revocation certificates
+ * are handled here.
+ *
+ * keyblock points to the first node in the keyblock,
+ * uidnode is the node with the user id to check
+ * keyid is the keyid of
+ * the primary key, drec is the directory record and recno_list
+ * is a list used to keep track of visited records.
+ * Existing uidflags are recalculated if recheck is true.
+ */
static void
-upd_uid_record( PKT_user_id *uid, TRUSTREC *drec, RECNO_LIST *recno_list,
- u32 *keyid, ulong *uidrecno, byte *uidhash )
+upd_uid_record( KBNODE keyblock, KBNODE uidnode, u32 *keyid,
+ TRUSTREC *drec, RECNO_LIST *recno_list, int recheck )
{
+ ulong lid = drec->recnum;
+ PKT_user_id *uid = uidnode->pkt->pkt.user_id;
TRUSTREC urec;
+ byte uidhash[20];
ulong recno, newrecno;
+ /* see whether we already have an uid record */
+ WORK WORK
rmd160_hash_buffer( uidhash, uid->name, uid->len );
for( recno=drec->r.dir.uidlist; recno; recno = urec.r.uid.next ) {
read_record( recno, &urec, RECTYPE_UID );
if( !memcmp( uidhash, urec.r.uid.namehash, 20 ) )
break;
}
if( recno ) {
ins_recno_list( recno_list, recno, RECTYPE_UID );
*uidrecno = recno;
}
else { /* new user id */
memset( &urec, 0 , sizeof(urec) );
urec.rectype = RECTYPE_UID;
urec.r.uid.lid = drec->recnum;
memcpy(urec.r.uid.namehash, uidhash, 20 );
urec.recnum = newrecno = tdbio_new_recnum();
write_record( &urec );
ins_recno_list( recno_list, newrecno, RECTYPE_UID );
/* and put this new record at the end of the uidlist */
if( !(recno=drec->r.dir.uidlist) ) { /* this is the first uid */
drec->r.dir.uidlist = newrecno;
drec->dirty = 1;
}
else { /* we already have an uid, append it to the list */
for( ; recno; recno = urec.r.key.next )
read_record( recno, &urec, RECTYPE_UID );
urec.r.uid.next = newrecno;
write_record( &urec );
}
*uidrecno = newrecno;
}
}
static void
upd_pref_record( PKT_signature *sig, ulong lid, const u32 *keyid,
TRUSTREC *urec, const byte *uidhash )
{
static struct {
sigsubpkttype_t subpkttype;
int preftype;
} prefs[] = {
{ SIGSUBPKT_PREF_SYM, PREFTYPE_SYM },
{ SIGSUBPKT_PREF_HASH, PREFTYPE_HASH },
{ SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR },
{ 0, 0 }
};
TRUSTREC prec;
const byte *s;
size_t n;
int k, i;
ulong recno_tbl[10];
int recno_idx = 0;
ulong recno;
/* First 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
- * FIXME: We never get correct statistics if we di it like this */
+ * FIXME: We never get correct statistics if we do it like this */
for( recno=urec->r.uid.prefrec; recno; recno = prec.r.pref.next ) {
read_record( recno, &prec, RECTYPE_PREF );
delete_record( recno );
}
/* and write the new ones */
i = 0;
for(k=0; prefs[k].subpkttype; k++ ) {
s = parse_sig_subpkt2( sig, prefs[k].subpkttype, &n );
if( s ) {
while( n ) {
if( !i || i >= ITEMS_PER_PREF_RECORD ) {
if( recno_idx >= DIM(recno_tbl)-1 ) {
log_info("too many preferences\n");
break;
}
if( i ) {
recno_tbl[recno_idx]=tdbio_new_recnum();
prec.recnum = recno_tbl[recno_idx++];
write_record( &prec );
}
memset( &prec, 0, sizeof prec );
prec.rectype = RECTYPE_PREF;
prec.r.pref.lid = lid;
i = 0;
}
prec.r.pref.data[i++] = prefs[k].preftype;
prec.r.pref.data[i++] = *s++;
n--;
}
}
}
if( i ) { /* write the last one */
recno_tbl[recno_idx]=tdbio_new_recnum();
prec.recnum = recno_tbl[recno_idx++];
write_record( &prec );
}
/* now link them together */
for(i=0; i < recno_idx-1; i++ ) {
read_record( recno_tbl[i], &prec, RECTYPE_PREF );
prec.r.pref.next = recno_tbl[i+1];
write_record( &prec );
}
/* don't need to write the last one, but update the uid */
urec->r.uid.prefrec = recno_idx? recno_tbl[0] : 0;
urec->dirty = 1;
}
/****************
* update self key signatures (class 0x10..0x13)
*/
static void
upd_self_key_sigs( PKT_signature *sig, TRUSTREC *urec,
ulong lid, const u32 *keyid, const byte *uidhash,
KBNODE keyblock, KBNODE signode)
{
int rc;
/* must verify this selfsignature here, so that we can
* build the preference record and validate the uid record
*/
if( !(urec->r.uid.uidflags & UIDF_CHECKED) ) {
rc = check_key_signature( keyblock, signode, NULL );
if( !rc ) {
if( opt.verbose )
log_info(_(
"key %08lX.%lu, uid %02X%02X: Good self-signature\n"),
(ulong)keyid[1], lid, uidhash[18], uidhash[19] );
upd_pref_record( sig, lid, keyid, urec, uidhash );
urec->r.uid.uidflags = UIDF_CHECKED | UIDF_VALID;
}
else {
log_info(_("key %08lX, uid %02X%02X: Invalid self-signature: %s\n"),
(ulong)keyid[1], uidhash[18], uidhash[19], g10_errstr(rc) );
urec->r.uid.uidflags = UIDF_CHECKED;
}
urec->dirty = 1;
}
}
+
/****************
* update non-self key signatures (class 0x10..0x13)
*/
static void
upd_nonself_key_sigs( PKT_signature *sig, TRUSTREC *urec,
ulong lid, const u32 *keyid, const byte *uidhash,
- KBNODE keyblock, KBNODE signode)
+ KBNODE keyblock, KBNODE signode, int fast)
{
/* We simply insert the signature into the sig records but
* avoid duplicate ones. We do not check them here because
* there is a big chance, that we import required public keys
* later. The problem with this is that we must somewhere store
* the information about this signature (we need a record id).
* We do this by using the record type shadow dir, which will
* be converted to a dir record as soon as a new public key is
* inserted into the trustdb.
*/
TRUSTREC rec;
ulong recno;
TRUSTREC delrec;
int delrecidx;
int newflag = 0;
ulong newlid = 0;
PKT_public_key *pk = m_alloc_clear( sizeof *pk );
ulong pk_lid = 0;
int found_sig = 0;
int found_delrec = 0;
int rc;
delrec.recnum = 0;
/* get the LID of the pubkey of the signature under verification */
rc = get_pubkey( pk, sig->keyid );
if( !rc ) {
if( pk->local_id )
pk_lid = pk->local_id;
else {
rc = tdbio_search_dir_bypk( pk, &rec );
if( !rc )
pk_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 )
pk_lid = rec.recnum;
}
}
}
free_public_key( pk ); pk = NULL;
/* Loop over all signatures just in case one is not correctly
* marked. If we see the correct signature, set a flag.
* delete duplicate signatures (should not happen but...) */
for( recno = urec->r.uid.siglist; recno; recno = rec.r.sig.next ) {
int i;
read_record( recno, &rec, RECTYPE_SIG );
for(i=0; i < SIGS_PER_RECORD; i++ ) {
TRUSTREC tmp;
if( !rec.r.sig.sig[i].lid ) {
if( !found_delrec && !delrec.recnum ) {
delrec = rec;
delrecidx = i;
found_delrec=1;
}
continue; /* skip deleted sigs */
}
if( rec.r.sig.sig[i].lid == pk_lid ) {
if( found_sig ) {
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
"duplicated signature - deleted\n"),
(ulong)keyid[1], lid, uidhash[18],
uidhash[19], (ulong)sig->keyid[1] );
rec.r.sig.sig[i].lid = 0;
rec.dirty = 1;
continue;
}
found_sig = 1;
}
- if( rec.r.sig.sig[i].flag & SIGF_CHECKED )
+ if( fast && (rec.r.sig.sig[i].flag & SIGF_CHECKED) )
continue; /* we already checked this signature */
- if( rec.r.sig.sig[i].flag & SIGF_NOPUBKEY )
+ if( fast && (rec.r.sig.sig[i].flag & SIGF_NOPUBKEY) )
continue; /* we do not have the public key */
read_record( rec.r.sig.sig[i].lid, &tmp, 0 );
if( tmp.rectype == RECTYPE_DIR ) {
/* In this case we should now be able to check the signature */
rc = check_key_signature( keyblock, signode, NULL );
if( !rc ) { /* valid signature */
if( opt.verbose )
log_info(_(
"key %08lX.%lu, uid %02X%02X, sig %08lX: "
"Good signature (1)\n"),
(ulong)keyid[1], lid, uidhash[18],
uidhash[19], (ulong)sig->keyid[1] );
rec.r.sig.sig[i].flag = SIGF_CHECKED | SIGF_VALID;
}
else if( rc == G10ERR_NO_PUBKEY ) {
- log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
- "weird: no public key\n"),
- (ulong)keyid[1], lid, uidhash[18],
- uidhash[19], (ulong)sig->keyid[1] );
+ if( (rec.r.sig.sig[i].flag & SIGF_CHECKED) )
+ log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
+ "public key lost\n"),
+ (ulong)keyid[1], lid, uidhash[18],
+ uidhash[19], (ulong)sig->keyid[1] );
rec.r.sig.sig[i].flag = SIGF_NOPUBKEY;
}
else {
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX:"
" %s\n"),
(ulong)keyid[1], lid, uidhash[18],
uidhash[19], (ulong)sig->keyid[1],
g10_errstr(rc));
rec.r.sig.sig[i].flag = SIGF_CHECKED;
}
rec.dirty = 1;
}
else if( tmp.rectype == RECTYPE_SDIR ) {
/* must 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 )) {
- log_info(_("key %08lX.%lu, uid %02X%02X: "
- "has shadow dir %lu but not yet marked.\n"),
+ if( !(rec.r.sig.sig[i].flag & SIGF_NOPUBKEY) )
+ log_info(_("key %08lX.%lu, uid %02X%02X: "
+ "has shadow dir %lu but not yet marked.\n"),
(ulong)keyid[1], lid,
uidhash[18], uidhash[19], tmp.recnum );
rec.r.sig.sig[i].flag = SIGF_NOPUBKEY;
rec.dirty = 1;
/* fixme: should we verify that the record is
* in the hintlist? - This case here should anyway
* never occur */
}
}
else {
log_error("sig record %lu[%d] points to wrong record.\n",
rec.r.sig.sig[i].lid, i );
die_invalid_db();
}
}
if( found_delrec && delrec.recnum ) {
delrec = rec;
found_delrec = 0; /* we only want the first one */
}
if( rec.dirty ) {
write_record( &rec );
rec.dirty = 0;
}
}
if( found_sig )
return;
/* at this point, we have verified, that the signature is not in
* our list of signatures. Add a new record with that signature
* and if the public key is there, check the signature. */
if( !pk_lid ) /* we have already seen that there is no pubkey */
rc = G10ERR_NO_PUBKEY;
else
rc = check_key_signature( keyblock, signode, NULL );
if( !rc ) { /* valid signature */
if( opt.verbose )
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
"Good signature (2)\n"),
(ulong)keyid[1], lid, uidhash[18],
uidhash[19], (ulong)sig->keyid[1] );
newlid = pk_lid; /* this is the pk of the signature */
newflag = SIGF_CHECKED | SIGF_VALID;
}
else if( rc == G10ERR_NO_PUBKEY ) {
if( opt.verbose > 1 )
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: "
"no public key\n"),
(ulong)keyid[1], lid, uidhash[18],
uidhash[19], (ulong)sig->keyid[1] );
newlid = create_shadow_dir( sig, lid );
newflag = SIGF_NOPUBKEY;
}
else {
log_info(_("key %08lX.%lu, uid %02X%02X, sig %08lX: %s\n"),
(ulong)keyid[1], lid, uidhash[18], uidhash[19],
(ulong)sig->keyid[1], g10_errstr(rc));
newlid = create_shadow_dir( sig, lid );
newflag = SIGF_CHECKED;
}
if( delrec.recnum ) { /* we can reuse a deleted slot */
delrec.r.sig.sig[delrecidx].lid = newlid;
delrec.r.sig.sig[delrecidx].flag= newflag;
write_record( &delrec );
}
else { /* must insert a new sig record */
TRUSTREC tmp;
memset( &tmp, 0, sizeof tmp );
tmp.recnum = tdbio_new_recnum();
tmp.rectype = RECTYPE_SIG;
tmp.r.sig.lid = lid;
tmp.r.sig.next = urec->r.uid.siglist;
tmp.r.sig.sig[0].lid = newlid;
tmp.r.sig.sig[0].flag= newflag;
write_record( &tmp );
urec->r.uid.siglist = tmp.recnum;
urec->dirty = 1;
}
}
/****************
* Note: A signature made with a secondary key is not considered a
* self-signature.
*/
static void
upd_sig_record( PKT_signature *sig, TRUSTREC *drec,
u32 *keyid, ulong *uidrecno, byte *uidhash,
- KBNODE keyblock, KBNODE signode)
+ KBNODE keyblock, KBNODE signode, int fast)
{
TRUSTREC urec;
ulong lid = drec->recnum;
if( !*uidrecno ) {
switch( sig->sig_class ) {
case 0x20:
case 0x28: /* We do not need uids for [sub]key revications */
case 0x18: /* or subkey binding */
memset( &urec, 0, sizeof urec ); /* to catch errors */
break;
default:
log_error("key %08lX: signature (class %02x) without user id\n",
(ulong)keyid[1], sig->sig_class );
return;
}
}
else
read_record( *uidrecno, &urec, RECTYPE_UID );
if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
if( (sig->sig_class&~3) == 0x10 ) {
upd_self_key_sigs( sig, &urec, lid, keyid, uidhash,
keyblock, signode );
}
else if( sig->sig_class == 0x18 ) { /* key binding */
- /* get the corresponding key */
-
- /* FIXME */
}
else if( sig->sig_class == 0x20 ) { /* key revocation */
/* FIXME */
}
else if( sig->sig_class == 0x28 ) { /* subkey revocation */
/* FIXME */
}
else if( sig->sig_class == 0x30 ) { /* cert revocation */
/* FIXME */
}
}
else if( (sig->sig_class&~3) == 0x10 ) {
upd_nonself_key_sigs( sig, &urec, lid, keyid, uidhash,
- keyblock, signode );
+ keyblock, signode, fast );
}
else if( sig->sig_class == 0x18 ) { /* key binding */
log_info(_("key %08lX: bogus key binding by %08lX\n"),
(ulong)keyid[1], (ulong)sig->keyid[1] );
}
else if( sig->sig_class == 0x20 ) { /* key revocation */
log_info(_("key %08lX: bogus key revocation by %08lX\n"),
(ulong)keyid[1], (ulong)sig->keyid[1] );
}
else if( sig->sig_class == 0x28 ) { /* subkey revocation */
log_info(_("key %08lX: bogus subkey revocation by %08lX\n"),
(ulong)keyid[1], (ulong)sig->keyid[1] );
}
else if( sig->sig_class == 0x30 ) { /* cert revocation */
/* fixme: a signator wants to revoke his certification signature */
}
if( urec.dirty ) {
write_record( &urec );
urec.dirty = 0;
}
}
/****************
* Update all the info from the public keyblock.
* The key must already exist in the keydb.
* This function is responsible for checking the signatures in cases
- * where the public key is already available. If we no not have the public
+ * where the public key is already available. If we do not have the public
* key, the check is done by some special code in insert_trust_record().
*/
int
-update_trust_record( KBNODE keyblock, int *modified )
+update_trust_record( KBNODE keyblock, int recheck, int *modified )
{
PKT_public_key *primary_pk;
KBNODE node;
TRUSTREC drec;
TRUSTREC krec;
TRUSTREC urec;
TRUSTREC prec;
TRUSTREC helprec;
int rc = 0;
u32 keyid[2]; /* keyid of primary key */
ulong recno, lastrecno;
ulong uidrecno = 0;
byte uidhash[20];
RECNO_LIST recno_list = NULL; /* list of verified records */
/* fixme: replace recno_list by a lookup on node->recno */
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 );
+ /* fixme: check that the keyblock has a valid structure */
+
rc = tdbio_begin_transaction();
if( rc )
return rc;
- /* now update keys and user ids */
+ /* update the keys */
for( node=keyblock; node; node = node->next ) {
- switch( node->pkt->pkttype ) {
- case PKT_PUBLIC_KEY:
- case PKT_PUBLIC_SUBKEY:
- uidrecno = 0;
- upd_key_record( node->pkt->pkt.public_key, &drec, &recno_list );
- break;
-
- case PKT_USER_ID:
- if( drec.dirty ) { /* upd_pref_record may read the drec */
- write_record( &drec );
- drec.dirty = 0;
- }
- upd_uid_record( node->pkt->pkt.user_id, &drec, &recno_list,
- keyid, &uidrecno, uidhash );
- break;
-
- case PKT_SIGNATURE:
- if( drec.dirty ) { /* upd_sig_recory may read the drec */
- write_record( &drec );
- drec.dirty = 0;
- }
- upd_sig_record( node->pkt->pkt.signature, &drec, keyid,
- &uidrecno, uidhash, keyblock, node );
- break;
-
- default:
- break;
- }
- } /* end loop over all nodes */
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ upd_key_record( keyblock, node, keyid,
+ &drec, &recno_list, recheck );
+ }
+ /* update the user IDs */
+ for( node=keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID )
+ upd_uid_record( keyblock, node, keyid,
+ &drec, &recno_list, recheck );
+ }
+ WORk - WORK
/* delete keyrecords from the trustdb which are not anymore used */
+ /* should we really do this, or is it better to keep them and */
+ /* mark as unused? */
lastrecno = 0;
for( recno=drec.r.dir.keylist; recno; recno = krec.r.key.next ) {
read_record( recno, &krec, RECTYPE_KEY );
if( !qry_recno_list( recno_list, recno, RECTYPE_KEY ) ) {
/* delete this one */
if( !lastrecno ) {
drec.r.dir.keylist = krec.r.key.next;
drec.dirty = 1;
}
else {
read_record( lastrecno, &helprec, RECTYPE_KEY );
helprec.r.key.next = krec.r.key.next;
write_record( &helprec );
}
delete_record( recno );
}
else
lastrecno = recno;
}
/* delete uid records and sig and their pref records from the
* trustdb which are not anymore used */
lastrecno = 0;
for( recno=drec.r.dir.uidlist; recno; recno = urec.r.uid.next ) {
read_record( recno, &urec, RECTYPE_UID );
if( !qry_recno_list( recno_list, recno, RECTYPE_UID ) ) {
ulong r2;
/* delete this one */
if( !lastrecno ) {
drec.r.dir.uidlist = urec.r.uid.next;
drec.dirty = 1;
}
else {
read_record( lastrecno, &helprec, RECTYPE_UID );
helprec.r.uid.next = urec.r.uid.next;
write_record( &helprec );
}
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 );
}
else
lastrecno = recno;
}
if( rc )
rc = tdbio_cancel_transaction();
else {
if( drec.dirty ) {
drec.r.dir.dirflags &= ~DIRF_CHECKED; /* reset flag */
write_record( &drec );
}
if( modified && tdbio_is_dirty() )
*modified = 0;
rc = tdbio_end_transaction();
}
rel_recno_list( &recno_list );
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 *pk )
{
TRUSTREC dirrec;
TRUSTREC shadow;
KBNODE keyblock = NULL;
KBNODE node;
byte fingerprint[MAX_FINGERPRINT_LEN];
size_t fingerlen;
int rc = 0;
ulong hintlist = 0;
if( pk->local_id )
log_bug("pk->local_id=%lu\n", pk->local_id );
fingerprint_from_pk( 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. 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.)
*/
/* 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;
}
/* check that we used the primary key (we are little bit paranoid) */
{ PKT_public_key *a_pk;
u32 akid[2], bkid[2];
node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
a_pk = node->pkt->pkt.public_key;
/* we can't use cmp_public_keys here because some parts (expiredate)
* might not be set in pk <--- but why (fixme) */
keyid_from_pk( a_pk, akid );
keyid_from_pk( pk, bkid );
if( akid[0] != bkid[0] || akid[1] != bkid[1] ) {
log_error("did not use primary key for insert_trust_record()\n");
rc = G10ERR_GENERAL;
goto leave;
}
}
/* We have to look for a shadow dir record which must be reused
* as the dir record. And: check all signatures which are listed
* in the hintlist of the shadow 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));
die_invalid_db();
}
memset( &dirrec, 0, sizeof dirrec );
dirrec.rectype = RECTYPE_DIR;
if( !rc ) {
/* hey, great: this key has already signed other keys
* convert this to a real directory entry */
hintlist = shadow.r.sdir.hintlist;
dirrec.recnum = shadow.recnum;
}
else {
dirrec.recnum = tdbio_new_recnum();
}
dirrec.r.dir.lid = dirrec.recnum;
write_record( &dirrec );
/* store the LID */
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 *pk = node->pkt->pkt.public_key;
pk->local_id = dirrec.r.dir.lid;
}
else if( node->pkt->pkttype == PKT_SIGNATURE ) {
PKT_signature *sig = node->pkt->pkt.signature;
sig->local_id = dirrec.r.dir.lid;
}
}
/* and put all the other stuff into the keydb */
- rc = update_trust_record( keyblock, NULL );
+ rc = update_trust_record( keyblock, 0, NULL );
if( !rc )
process_hintlist( hintlist, dirrec.r.dir.lid );
leave:
if( rc && hintlist )
; /* fixme: the hintlist is not anymore anchored */
release_kbnode( keyblock );
do_sync();
return rc;
}
int
update_ownertrust( ulong lid, unsigned new_trust )
{
TRUSTREC rec;
read_record( lid, &rec, RECTYPE_DIR );
rec.r.dir.ownertrust = new_trust;
write_record( &rec );
do_sync();
return 0;
}
diff --git a/g10/trustdb.h b/g10/trustdb.h
index 01d3dab55..4dc8b2ccb 100644
--- a/g10/trustdb.h
+++ b/g10/trustdb.h
@@ -1,69 +1,69 @@
/* 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 PREFTYPE_SYM 1
#define PREFTYPE_HASH 2
#define PREFTYPE_COMPR 3
/*-- trustdb.c --*/
void list_trustdb(const char *username);
void list_trust_path( int max_depth, const char *username );
void export_ownertrust(void);
void import_ownertrust(const char *fname);
void check_trustdb( const char *username );
void update_trustdb( void );
int init_trustdb( int level, const char *dbname );
int check_trust( PKT_public_key *pk, unsigned *r_trustlevel );
int query_trust_info( PKT_public_key *pk );
int enum_trust_web( void **context, ulong *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 *modified );
+int update_trust_record( KBNODE keyblock, int fast, int *modified );
int update_ownertrust( ulong lid, unsigned new_trust );
/*-- pkclist.c --*/
int edit_ownertrust( ulong lid, int mode );
#endif /*G10_TRUSTDB_H*/
diff --git a/include/util.h b/include/util.h
index 5dd656e72..56098ae6e 100644
--- a/include/util.h
+++ b/include/util.h
@@ -1,192 +1,194 @@
/* 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 index;
int inarg;
int stopped;
const char *last;
} 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_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, 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 );
/*-- 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 *strtimestamp( u32 stamp ); /* GMT */
const char *asctimestamp( u32 stamp ); /* localized */
void print_string( FILE *fp, byte *p, size_t n, int delim );
int answer_is_yes( const char *s );
/*-- strgutil.c --*/
void free_strlist( STRLIST sl );
#define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0)
STRLIST add_to_strlist( STRLIST *list, const char *string );
STRLIST append_to_strlist( STRLIST *list, const char *string );
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 );
int string_count_chr( const char *string, int c );
+char *native_to_utf8( const char *string );
+char *utf8_to_native( const char *string );
#define stricmp(a,b) strcasecmp((a),(b))
#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
/**** 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/mpi/ChangeLog b/mpi/ChangeLog
index 73f296d69..893c4c28e 100644
--- a/mpi/ChangeLog
+++ b/mpi/ChangeLog
@@ -1,85 +1,89 @@
+Tue Oct 27 12:37:46 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * config.links (freebsd): Fixes for FreeBSD 3.0
+
Wed Oct 14 09:59:30 1998 Werner Koch (wk@isil.d.shuttle.de)
- * config-links (freebsd): ELF patches from Jun Kuriyama.
+ * config.links (freebsd): ELF patches from Jun Kuriyama.
Thu Oct 8 13:28:17 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpi-mpow.c (mpi_mulpowm): Fixed mem leak (m_free/mpi_free).
Thu Sep 17 18:08:50 1998 Werner Koch (wk@(none))
* hppa1.1/udiv-qrnnd.S: Fix from Steffen Zahn for HPUX 10.20
Thu Aug 6 16:39:28 1998 Werner Koch,mobil,,, (wk@tobold)
* mpi-bit.c (mpi_set_bytes): Removed.
Wed Aug 5 15:11:12 1998 Werner Koch (wk@(none))
* mpicoder.c (mpi_read_from_buffer): New.
* mpiutil.c (mpi_set_opaque): New.
(mpi_get_opaque): New.
(mpi_copy): Changed to support opauqe flag
(mpi_free): Ditto.
Sat Jul 4 10:11:11 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpiutil.c (mpi_clear): Reset flags.
(mpi_set): Ditto.
(mpi_alloc_secure): Set flag to 1 and not ored the 1 in, tsss..
Fri Jun 26 11:19:06 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpiutil.c (mpi_alloc): set nbits to 0.
(mpi_alloc_secure): Ditto.
(mpi_clear): Ditto.
Thu Jun 25 11:50:01 1998 Werner Koch (wk@isil.d.shuttle.de)
* mips3/*.S: New
Mon May 18 13:47:06 1998 Werner Koch (wk@isil.d.shuttle.de)
* config.links: split mpih-shift into mpih-[lr]shift and
changed all implementations.
* mpi/alpha: add some new assembler stuff.
Wed May 13 11:04:29 1998 Werner Koch (wk@isil.d.shuttle.de)
* config.links: Add support for MIPS
Thu Apr 9 11:31:36 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpicoder.c (mpi_get_secure_buffer): New.
Wed Apr 8 09:44:33 1998 Werner Koch (wk@isil.d.shuttle.de)
* config.links: Applied small fix from Ulf Möller.
Mon Apr 6 12:38:52 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpicoder.c (mpi_get_buffer): Removed returned leading zeroes
and changed all callers.
Tue Mar 10 13:40:34 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpi-bit.c (mpi_clear_highbit): New.
Mon Mar 2 19:29:00 1998 Werner Koch (wk@isil.d.shuttle.de)
* Makefile.am (DISTCLEANFILES): New
Thu Feb 26 06:48:54 1998 Werner Koch (wk@isil.d.shuttle.de)
* config.links (X86_BROKEN_ALIGN): Added for some systems.
Mon Feb 23 12:21:40 1998 Werner Koch (wk@isil.d.shuttle.de)
* mpi/m68k/mpih-shift.S (Lspecial): Changed duplicate symbol.
Mon Feb 16 13:00:27 1998 Werner Koch (wk@isil.d.shuttle.de)
* config.links : Add detection of m68k cpus
diff --git a/mpi/config.links b/mpi/config.links
index 5c3f960df..308733c21 100644
--- a/mpi/config.links
+++ b/mpi/config.links
@@ -1,187 +1,187 @@
# sourced my ../configure to get the list of files to link
# this should set $mpi_ln_src and mpi_ln_dst.
# Note: this is called from the above directory.
mpi_extra_modules=
test -d ./mpi || mkdir ./mpi
echo '/* created by config.links - do not edit */' >./mpi/asm-syntax.h
case "${target}" in
- i[34]86*-*-freebsd*-elf)
+ i[34]86*-*-freebsd*-elf | i[34]86*-*-freebsd3*)
echo '#define ELF_SYNTAX' >>./mpi/asm-syntax.h
cat $srcdir/mpi/i386/syntax.h >>./mpi/asm-syntax.h
path="i386"
;;
- i[56]86*-*-freebsd*-elf)
+ i[56]86*-*-freebsd*-elf | i[56]86*-*-freebsd3*)
echo '#define ELF_SYNTAX' >>./mpi/asm-syntax.h
cat $srcdir/mpi/i386/syntax.h >>./mpi/asm-syntax.h
path="i586 i386"
;;
i[34]86*-*-linuxaout* | i[34]86*-*-linuxoldld* | i[34]86*-*-*bsd*)
echo '#define BSD_SYNTAX' >>./mpi/asm-syntax.h
echo '#define X86_BROKEN_ALIGN' >>./mpi/asm-syntax.h
cat $srcdir/mpi/i386/syntax.h >>./mpi/asm-syntax.h
path="i386"
;;
i[56]86*-*-linuxaout* | i[56]86*-*-linuxoldld* | i[56]86*-*-*bsd*)
echo '#define BSD_SYNTAX' >>./mpi/asm-syntax.h
echo '#define X86_BROKEN_ALIGN' >>./mpi/asm-syntax.h
cat $srcdir/mpi/i386/syntax.h >>./mpi/asm-syntax.h
path="i586 i386"
;;
i[34]86*-*-*)
echo '#define ELF_SYNTAX' >>./mpi/asm-syntax.h
cat $srcdir/mpi/i386/syntax.h >>./mpi/asm-syntax.h
path="i386"
;;
i[56]86*-*-* | pentium-*-* | pentiumpro-*-*)
echo '#define ELF_SYNTAX' >>./mpi/asm-syntax.h
cat $srcdir/mpi/i386/syntax.h >>./mpi/asm-syntax.h
path="i586 i386"
;;
alpha*-*-*)
echo '/* configured for alpha */' >>./mpi/asm-syntax.h
path="alpha"
mpi_extra_modules="udiv-qrnnd"
;;
hppa7000*-*-*)
echo '/* configured for HPPA (pa7000) */' >>./mpi/asm-syntax.h
path="hppa1.1 hppa"
mpi_extra_modules="udiv-qrnnd"
;;
hppa1.0*-*-*)
echo '/* configured for HPPA 1.0 */' >>./mpi/asm-syntax.h
path="hppa"
mpi_extra_modules="udiv-qrnnd"
;;
hppa*-*-*) # assume pa7100
echo '/* configured for HPPA (pa7100) */' >>./mpi/asm-syntax.h
path="pa7100 hppa1.1 hppa"
mpi_extra_modules="udiv-qrnnd"
;;
sparc9*-*-* | sparc64*-*-* | ultrasparc*-*-*)
echo '/* configured for sparc9 or higher */' >>./mpi/asm-syntax.h
path="sparc32v8 sparc32"
;;
sparc8*-*-* | microsparc*-*-*)
echo '/* configured for sparc8 */' >>./mpi/asm-syntax.h
path="sparc32v8"
;;
supersparc*-*-*)
echo '/* configured for supersparc */' >>./mpi/asm-syntax.h
path="supersparc sparc32v8 sparc32"
mpi_extra_modules="udiv"
;;
sparc*-*-*)
echo '/* configured for sparc */' >>./mpi/asm-syntax.h
path="sparc32"
mpi_extra_modules="udiv"
;;
mips[34]*-*-* | mips*-*-irix6*)
echo '/* configured for MIPS3 */' >>./mpi/asm-syntax.h
path="mips3"
;;
mips*-*-*)
echo '/* configured for MIPS2 */' >>./mpi/asm-syntax.h
path="mips2"
;;
# Motorola 68k configurations. Let m68k mean 68020-68040.
# mc68000 or mc68060 configurations need to be specified explicitly
m680[234]0*-*-linuxaout* | m68k*-*-linuxaout*)
echo '#define MIT_SYNTAX' >./mpi/asm-syntax.h
cat $srcdir/mpi/m68k/syntax.h >>./mpi/asm-syntax.h
path="m68k/mc68020 m68k"
;;
m68060*-*-linuxaout*)
echo '#define MIT_SYNTAX' >./mpi/asm-syntax.h
cat $srcdir/mpi/m68k/syntax.h >>./mpi/asm-syntax.h
path="m68k"
;;
m680[234]0*-*-linux* | m68k*-*-linux*)
echo '#define ELF_SYNTAX' >./mpi/asm-syntax.h
cat $srcdir/mpi/m68k/syntax.h >>./mpi/asm-syntax.h
path="m68k/mc68020 m68k"
;;
m68060*-*-linux*)
echo '#define ELF_SYNTAX' >./mpi/asm-syntax.h
cat $srcdir/mpi/m68k/syntax.h >>./mpi/asm-syntax.h
path="m68k"
;;
m68000*-*-* | m68060*-*-*)
echo '#define MIT_SYNTAX' >./mpi/asm-syntax.h
cat $srcdir/mpi/m68k/syntax.h >>./mpi/asm-syntax.h
path="m68k/mc68000"
;;
m680[234]0*-*-* | m68k*-*-*)
echo '#define MIT_SYNTAX' >./mpi/asm-syntax.h
cat $srcdir/mpi/m68k/syntax.h >>./mpi/asm-syntax.h
path="m68k/mc68020 m68k"
;;
*)
echo '/* No assembler modules configured */' >>./mpi/asm-syntax.h
path=""
;;
esac
case "${target}" in
*-*-linuxaout* | *-*-linuxoldld* | *-*-linux-gnuoldld*)
needs_underscore="y"
;;
*-*-linux* | *-sysv* | *-solaris* | *-gnu* | *-freebsd*-elf)
needs_underscore="n"
;;
*)
needs_underscore="y"
;;
esac
# Make sysdep.h
echo '/* created by config.links - do not edit */' >./mpi/sysdep.h
if test "$needs_underscore" = "y" ; then
cat <<EOF >>./mpi/sysdep.h
#if __STDC__
#define C_SYMBOL_NAME(name) _##name
#else
#define C_SYMBOL_NAME(name) _/**/name
#endif
EOF
else
cat <<EOF >>./mpi/sysdep.h
#define C_SYMBOL_NAME(name) name
EOF
fi
# fixme: grep these modules from Makefile.in
mpi_ln_modules="${mpi_extra_modules} mpih-add1 mpih-mul1 mpih-mul2 mpih-mul3 \
mpih-lshift mpih-rshift mpih-sub1"
mpi_ln_objects=
mpi_ln_src=
mpi_ln_dst=
# try to get file to link from the assembler subdirectory and
# if this fails get it from the generic subdirectory.
path="$path generic"
for fn in $mpi_ln_modules ; do
mpi_ln_objects="$mpi_ln_objects $fn.o"
for dir in $path ; do
rm -f $srcdir/mpi/$fn.[Sc]
if test -f $srcdir/mpi/$dir/$fn.S ; then
mpi_ln_src="$mpi_ln_src mpi/$dir/$fn.S"
mpi_ln_dst="$mpi_ln_dst mpi/$fn.S"
break;
elif test -f $srcdir/mpi/$dir/$fn.c ; then
mpi_ln_src="$mpi_ln_src mpi/$dir/$fn.c"
mpi_ln_dst="$mpi_ln_dst mpi/$fn.c"
break;
fi
done
done
diff --git a/po/ChangeLog b/po/ChangeLog
index 5c4611e71..b9ec290d3 100644
--- a/po/ChangeLog
+++ b/po/ChangeLog
@@ -1,46 +1,50 @@
+Fri Oct 30 20:03:17 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * fr.po: Imported new version
+
Mon Oct 12 09:08:09 1998 Werner Koch (wk@isil.d.shuttle.de)
* it.po: Imported new version.
* de.po: Imported new version.
Sun Oct 11 16:24:27 1998 Werner Koch (wk@isil.d.shuttle.de)
* fr.po: Imported new version
Wed Oct 7 13:12:00 1998 Werner Koch (wk@isil.d.shuttle.de)
* Makefile.in.in: Fixed mkinstalldirs problems
Mon Sep 21 15:03:44 1998 Werner Koch (wk@(none))
* it.po: New version from Marco d'Itri
Mon Sep 21 09:28:54 1998 Werner Koch (wk@(none))
* fr.po: New version from Gaël Quéri
Tue Aug 11 12:28:11 1998 Werner Koch (wk@(none))
* it.po: New file from Marco.
Thu Jul 9 21:14:51 1998 Werner Koch (wk@isil.d.shuttle.de)
* de.po: Fixed typos and added new translations.
Fri Jun 26 11:44:24 1998 Werner Koch (wk@isil.d.shuttle.de)
* it.po: New file from Marco.
Thu May 28 10:44:25 1998 Werner Koch (wk@isil.d.shuttle.de)
* it.po: Add small corrections from Marco
Thu Apr 9 10:03:14 1998 Werner Koch (wk@isil.d.shuttle.de)
* it.po: New version from Marco
Fri Mar 13 09:43:19 1998 Werner Koch (wk@isil.d.shuttle.de)
* it.po: New
diff --git a/po/fr.po b/po/fr.po
index f6c2bc064..9bb50bee5 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,2297 +1,2416 @@
-# gnupg french translation
+# GnuPG french translation
# Copyright (C) 1998 Free Software Foundation, Inc.
# Gaël Quéri <gqueri@mail.dotcom.fr>, 1998.
#
msgid ""
msgstr ""
-"Project-Id-Version: gnupg-0.4.1\n"
-"POT-Creation-Date: 1998-10-18 16:36+0200\n"
-"PO-Revision-Date: 1998-10-11 00:21+0200\n"
+"Project-Id-Version: gnupg-0.4.1a\n"
+"POT-Creation-Date: 1998-10-29 17:30+0100\n"
+"PO-Revision-Date: 1998-10-29 19:01+0100\n"
"Last-Translator: Gaël Quéri <gqueri@mail.dotcom.fr>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso8859-1\n"
"Content-Transfer-Encoding: 8-bit\n"
#: util/secmem.c:77
msgid "Warning: using insecure memory!\n"
msgstr "Attention: utilisation de la mémoire non sûre!\n"
#: util/miscutil.c:110
msgid "yes"
msgstr "oui"
#: util/miscutil.c:111
msgid "yY"
msgstr "oO"
#: util/errors.c:54
msgid "General error"
msgstr "Erreur Générale"
#: util/errors.c:55
msgid "Unknown packet type"
msgstr "Type de paquet inconnu"
#: util/errors.c:56
msgid "Unknown version"
msgstr "Version inconnue"
#: util/errors.c:57
msgid "Unknown pubkey algorithm"
msgstr "Algorithme de clé publique inconnu"
#: util/errors.c:58
msgid "Unknown digest algorithm"
msgstr "Algorithme de hachage inconnu"
#: util/errors.c:59
msgid "Bad public key"
msgstr "Mauvaise clé publique"
#: util/errors.c:60
msgid "Bad secret key"
msgstr "Mauvaise clé secrète"
#: util/errors.c:61
msgid "Bad signature"
msgstr "Mauvaise signature"
#: util/errors.c:62
msgid "Checksum error"
msgstr "Erreur de somme de vérification"
#: util/errors.c:63
msgid "Bad passphrase"
msgstr "Mauvais mot de passe"
#: util/errors.c:64
msgid "Public key not found"
msgstr "Clé publique non trouvée"
#: util/errors.c:65
msgid "Unknown cipher algorithm"
msgstr "Algorithme de chiffrement inconnu"
#: util/errors.c:66
msgid "Can't open the keyring"
msgstr "Ne peut ouvrir le porte-clés"
#: util/errors.c:67
msgid "Invalid packet"
msgstr "Paquet invalide"
#: util/errors.c:68
msgid "Invalid armor"
msgstr "Armure invalide"
#: util/errors.c:69
msgid "No such user id"
msgstr "Pas de tel utilisateur."
#: util/errors.c:70
msgid "Secret key not available"
msgstr "La clé secrète n'est pas disponible."
#: util/errors.c:71
msgid "Wrong secret key used"
msgstr "Mauvaise clé secrète utilisée"
#: util/errors.c:72
msgid "Not supported"
msgstr "Non supporté"
#: util/errors.c:73
msgid "Bad key"
msgstr "Mauvaise clé"
#: util/errors.c:74
msgid "File read error"
msgstr "Erreur de lecture"
#: util/errors.c:75
msgid "File write error"
msgstr "Erreur d'écriture"
#: util/errors.c:76
msgid "Unknown compress algorithm"
msgstr "Algorithme de compression inconnu"
#: util/errors.c:77
msgid "File open error"
msgstr "Erreur d'ouverture de fichier"
#: util/errors.c:78
msgid "File create error"
msgstr "Erreur de création de fichier"
#: util/errors.c:79
msgid "Invalid passphrase"
msgstr "Mot de passe invalide"
#: util/errors.c:80
msgid "Unimplemented pubkey algorithm"
msgstr "Algorithme de clé publique non implémenté"
#: util/errors.c:81
msgid "Unimplemented cipher algorithm"
msgstr "Algorithme de chiffrement non implémenté"
#: util/errors.c:82
msgid "Unknown signature class"
msgstr "Classe de signature inconnue"
#: util/errors.c:83
msgid "Trust database error"
msgstr "Erreur dans la base de confiance"
#: util/errors.c:84
msgid "Bad MPI"
msgstr "Mauvais MPI"
#: util/errors.c:85
msgid "Resource limit"
msgstr "Limite de ressources"
#: util/errors.c:86
msgid "Invalid keyring"
msgstr "Porte-clés invalide"
#: util/errors.c:87
msgid "Bad certificate"
msgstr "Mauvais certificat"
#: util/errors.c:88
msgid "Malformed user id"
msgstr "Nom d'utilisateur déformé"
#: util/errors.c:89
msgid "File close error"
msgstr "Erreur de fermeture de fichier"
#: util/errors.c:90
msgid "File rename error"
msgstr "Erreur pendant le changement de nom du fichier"
#: util/errors.c:91
msgid "File delete error"
msgstr "Erreur pendant la suppression du fichier"
#: util/errors.c:92
msgid "Unexpected data"
msgstr "Données inattendues"
#: util/errors.c:93
msgid "Timestamp conflict"
msgstr "Conflit de dates"
#: util/errors.c:94
msgid "Unusable pubkey algorithm"
msgstr "Algorithme de clés publiques inutilisable"
#: util/errors.c:95
msgid "File exists"
msgstr "Le fichier existe"
#: util/errors.c:96
msgid "Weak key"
msgstr "Mauvaise clé"
#: util/logger.c:177
#, c-format
-msgid "Ohhhh jeeee ... this is a bug (%s:%d:%s)\n"
-msgstr "Ouais ... c'est un bug (%s:%d:%s)\n"
+msgid "... this is a bug (%s:%d:%s)\n"
+msgstr "... c'est un bug (%s:%d:%s)\n"
#: util/logger.c:183
#, c-format
msgid "you found a bug ... (%s:%d)\n"
msgstr "vous avez trouvé un bug ... (%s:%d)\n"
#: cipher/rand-dummy.c:112
msgid "warning: using insecure random number generator!!\n"
msgstr ""
"attention: utilisation d'un générateur de nombres aléatoires peu sûr!!\n"
#: cipher/rand-dummy.c:113
msgid ""
"The random number generator is only a kludge to let\n"
"it compile - it is in no way a strong RNG!\n"
"\n"
"DON'T USE ANY DATA GENERATED BY THIS PROGRAM!!\n"
"\n"
msgstr ""
"Le générateur de nombres aléatoires n'est qu'un artifice visant\n"
"à le compiler - ce n'est en aucune manière un générateur(RNG) fort!\n"
"\n"
"N'UTILISEZ PAS LES DONNÉES GÉNÉRÉES PAR CE PROGRAMME!!\n"
"\n"
#: cipher/rand-unix.c:149
#, c-format
msgid ""
"\n"
"Not enough random bytes available. Please do some other work to give\n"
"the OS a chance to collect more entropy! (Need %d more bytes)\n"
msgstr ""
"\n"
"Il n'y a pas assez d'octets aléatoires disponibles. Faites autre chose\n"
"pour que l'OS puisse amasser plus d'entropie! (il faut %d octets de plus)\n"
-#: g10/g10.c:149
+#: g10/g10.c:151
msgid ""
"@Commands:\n"
" "
msgstr ""
"@Commandes:\n"
" "
-#: g10/g10.c:152
+#: g10/g10.c:154
msgid "|[file]|make a signature"
msgstr "|[fichier]|faire une signature"
-#: g10/g10.c:153
+#: g10/g10.c:155
msgid "|[file]|make a clear text signature"
msgstr "|[fichier]|faire une signature en texte clair"
-#: g10/g10.c:154
+#: g10/g10.c:156
msgid "make a detached signature"
msgstr "faire une signature détachée"
-#: g10/g10.c:155
+#: g10/g10.c:157
msgid "encrypt data"
msgstr "crypter les données"
-#: g10/g10.c:156
+#: g10/g10.c:158
msgid "encryption only with symmetric cipher"
msgstr "chiffrement symétrique seumement"
-#: g10/g10.c:157
+#: g10/g10.c:159
msgid "store only"
msgstr "pas d'action"
-#: g10/g10.c:158
+#: g10/g10.c:160
msgid "decrypt data (default)"
msgstr "décrypter les données (défaut)"
-#: g10/g10.c:159
+#: g10/g10.c:161
msgid "verify a signature"
msgstr "vérifier une signature"
-#: g10/g10.c:161
+#: g10/g10.c:163
msgid "list keys"
msgstr "lister les clés"
-#: g10/g10.c:162
+#: g10/g10.c:164
msgid "list keys and signatures"
msgstr "lister les clés et les signatures"
-#: g10/g10.c:163
+#: g10/g10.c:165
msgid "check key signatures"
msgstr "vérifier les signatures des clés"
-#: g10/g10.c:164
+#: g10/g10.c:166
msgid "list keys and fingerprints"
msgstr "lister les clés et les empreintes"
-#: g10/g10.c:165
+#: g10/g10.c:167
msgid "list secret keys"
msgstr "lister les clés secrètes"
-#: g10/g10.c:167
+#: g10/g10.c:169
msgid "generate a new key pair"
msgstr "générer une nouvelle paire de clés"
-#: g10/g10.c:169
+#: g10/g10.c:171
msgid "remove key from the public keyring"
msgstr "enlever la clé du porte-clés public"
-#: g10/g10.c:171
+#: g10/g10.c:173
msgid "sign or edit a key"
msgstr "signer ou éditer une clé"
-#: g10/g10.c:172
+#: g10/g10.c:174
msgid "generate a revocation certificate"
msgstr "générer un certificat de révocation"
-#: g10/g10.c:174
+#: g10/g10.c:176
msgid "export keys"
msgstr "exporter les clés"
-#: g10/g10.c:176
+#: g10/g10.c:178
msgid "import/merge keys"
msgstr "importer/fusionner les clés"
-#: g10/g10.c:177
+#: g10/g10.c:180
msgid "list only the sequence of packets"
msgstr "ne lister qu'une suite de paquets"
-#: g10/g10.c:180
+#: g10/g10.c:183
msgid "export the ownertrust values"
msgstr "exporter les valeurs de confiance"
-#: g10/g10.c:182
+#: g10/g10.c:185
msgid "import ownertrust values"
msgstr "importer les valeurs de confiance"
-#: g10/g10.c:184
-#, fuzzy
+#
+#: g10/g10.c:187
msgid "|[NAMES]|update the trust database"
-msgstr "|[NOMS]|vérifier la base de confiance"
+msgstr "|[NOMS]|mettre la base de confiance à jour"
-#: g10/g10.c:186
+#: g10/g10.c:189
msgid "|[NAMES]|check the trust database"
msgstr "|[NOMS]|vérifier la base de confiance"
-#: g10/g10.c:187
+#: g10/g10.c:190
msgid "fix a corrupted trust database"
msgstr "réparer une base de confiance corrompue"
-#: g10/g10.c:188
+#: g10/g10.c:191
msgid "De-Armor a file or stdin"
msgstr "Enlever l'armure d'un fichier ou de stdin"
-#: g10/g10.c:189
+#: g10/g10.c:192
msgid "En-Armor a file or stdin"
msgstr "Mettre une armure à un fichier ou à stdin"
-#: g10/g10.c:190
+#: g10/g10.c:193
msgid "|algo [files]|print message digests"
msgstr "|alg. [fich.]|indiquer les fonctions de hachage"
-#: g10/g10.c:191
+#: g10/g10.c:194
msgid "print all message digests"
msgstr "écrire toutes les fonctions de hachage"
-#: g10/g10.c:198
+#: g10/g10.c:201
msgid ""
"@\n"
"Options:\n"
" "
msgstr ""
"@\n"
"Options:\n"
" "
-#: g10/g10.c:200
+#: g10/g10.c:203
msgid "create ascii armored output"
msgstr "créer une sortie ascii armurée"
-#: g10/g10.c:202
+#: g10/g10.c:205
msgid "use this user-id to sign or decrypt"
msgstr "utiliser ce nom pour signer ou décrypter"
-#: g10/g10.c:203
+#: g10/g10.c:206
msgid "use this user-id for encryption"
msgstr "utiliser ce nom d'utilisateur pour crypter"
-#: g10/g10.c:204
+#: g10/g10.c:207
msgid "|N|set compress level N (0 disables)"
msgstr "|N|niveau de compression N (0 désactive)"
-#: g10/g10.c:205
+#: g10/g10.c:208
msgid "use canonical text mode"
msgstr "utiliser le mode de texte canonique"
-#: g10/g10.c:207
+#: g10/g10.c:210
msgid "use as output file"
msgstr "utiliser comme fichier de sortie"
-#: g10/g10.c:208
+#: g10/g10.c:211
msgid "verbose"
msgstr "bavard"
-#: g10/g10.c:209
-#, fuzzy
+#: g10/g10.c:212
+msgid "be somewhat more quiet"
+msgstr "devenir beaucoup plus silencieux"
+
+#
+#: g10/g10.c:213
msgid "force v3 signatures"
-msgstr "vérifier les signatures des clés"
+msgstr "forcer les signatures en v3"
#. { oDryRun, "dry-run", 0, N_("do not make any changes") },
-#: g10/g10.c:211
+#: g10/g10.c:215
msgid "batch mode: never ask"
-msgstr "mode automatique: ne jamais demander"
+msgstr "mode automatique: ne jamais rien demander"
-#: g10/g10.c:212
+#: g10/g10.c:216
msgid "assume yes on most questions"
msgstr "supposer oui à la plupart des questions"
-#: g10/g10.c:213
+#: g10/g10.c:217
msgid "assume no on most questions"
msgstr "supposer non à la plupart des questions"
-#: g10/g10.c:214
+#: g10/g10.c:218
msgid "add this keyring to the list of keyrings"
msgstr "ajouter ce porte-clés à la liste des porte-clés"
-#: g10/g10.c:215
+#: g10/g10.c:219
msgid "add this secret keyring to the list"
msgstr "ajouter ce porte-clés secret à la liste"
-#: g10/g10.c:216
+#: g10/g10.c:220
msgid "|NAME|use NAME as default secret key"
msgstr "|NOM|utiliser NOM comme clé secrète par défaut"
-#: g10/g10.c:217
+#: g10/g10.c:221
msgid "read options from file"
msgstr "lire les options du fichier"
-#: g10/g10.c:219
+#: g10/g10.c:223
msgid "set debugging flags"
msgstr "choisir les attributs de déboguage"
-#: g10/g10.c:220
+#: g10/g10.c:224
msgid "enable full debugging"
msgstr "permettre un déboguage complet"
-#: g10/g10.c:221
+#: g10/g10.c:225
msgid "|FD|write status info to this FD"
msgstr "|FD|écrire les informations d'état sur ce descripteur"
-#: g10/g10.c:222
+#: g10/g10.c:226
msgid "do not write comment packets"
msgstr "ne pas écrire de paquets de commentaire"
-#: g10/g10.c:223
+#: g10/g10.c:227
msgid "(default is 1)"
msgstr "(1 par défaut)"
-#: g10/g10.c:224
+#: g10/g10.c:228
msgid "(default is 3)"
msgstr "(3 par défaut)"
-#: g10/g10.c:225
+#: g10/g10.c:229
msgid "|FILE|load extension module FILE"
msgstr "|FICH|charger le module d'extension FICH"
-#: g10/g10.c:226
+#: g10/g10.c:230
msgid "emulate the mode described in RFC1991"
msgstr "émuler le mode décrit dans la RFC1991"
-#: g10/g10.c:227
+#: g10/g10.c:231
msgid "|N|use passphrase mode N"
msgstr "|N|utiliser le mode de mots de passe N"
-#: g10/g10.c:229
+#: g10/g10.c:233
msgid "|NAME|use message digest algorithm NAME for passphrases"
msgstr "|NOM|utiliser le hachage NOM pour les mots de passe"
-#: g10/g10.c:231
+#: g10/g10.c:235
msgid "|NAME|use cipher algorithm NAME for passphrases"
msgstr "|NOM|utiliser le cryptage NOM pour les mots de passe"
-#: g10/g10.c:233
+#: g10/g10.c:237
msgid "|NAME|use cipher algorithm NAME"
msgstr "|NOM|utiliser l'algorithme de cryptage NOM"
-#: g10/g10.c:234
+#: g10/g10.c:238
msgid "|NAME|use message digest algorithm NAME"
msgstr "|NOM|utiliser la fonction de hachage NOM"
-#: g10/g10.c:235
+#: g10/g10.c:239
msgid "|N|use compress algorithm N"
msgstr "|N|utiliser l'algorithme de compression N"
-#: g10/g10.c:236
+#: g10/g10.c:240
msgid "throw keyid field of encrypted packets"
msgstr "enlever l'identification des paquets cryptés"
-#: g10/g10.c:244
+#: g10/g10.c:248
msgid ""
"@\n"
"Examples:\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"
msgstr ""
"@\n"
"Exemples:\n"
"\n"
" -se -r Alice [fichier] signer et crypter pour l'utilisateur Alice\n"
" --clearsign [fichier] faire une signature en texte clair\n"
" --detach-sign [fichier] faire une signature détachée\n"
" --list-keys [utilisateur] montrer les clés\n"
" --fingerprint [utilisateur] montrer les empreintes\n"
-#: g10/g10.c:319
+#: g10/g10.c:323
msgid "Please report bugs to <gnupg-bugs@gnu.org>.\n"
msgstr "Rapporter toutes anomalies à <gnupg-bugs@gnu.org>.\n"
-#: g10/g10.c:324
+#: g10/g10.c:328
msgid "Usage: gpgm [options] [files] (-h for help)"
msgstr "Utilisation: gpgm [options] [fichiers] (-h pour l'aide)"
-#: g10/g10.c:326
+#: g10/g10.c:330
msgid "Usage: gpg [options] [files] (-h for help)"
msgstr "Utilisation: gpg [options] [fichiers] (-h pour l'aide)"
-#: g10/g10.c:331
-#, fuzzy
+#
+#: g10/g10.c:335
msgid ""
"Syntax: gpgm [options] [files]\n"
"GnuPG maintenance utility\n"
msgstr ""
"Syntaxe: gpgm [options] [fichiers]\n"
-"utilitaire de maitenance de GNUPG\n"
+"utilitaire de maitenance de GnuPG\n"
-#: g10/g10.c:334
+#: g10/g10.c:338
msgid ""
"Syntax: gpg [options] [files]\n"
"sign, check, encrypt or decrypt\n"
"default operation depends on the input data\n"
msgstr ""
"Syntaxe: gpg [options] [fichiers]\n"
"signer, vérifier, crypter ou décrypter\n"
"l'opération par défaut dépend des données entrées\n"
-#: g10/g10.c:340
+#: g10/g10.c:344
msgid ""
"\n"
"Supported algorithms:\n"
msgstr ""
"\n"
"Algorithmes supportés:\n"
-#: g10/g10.c:415
+#: g10/g10.c:419
msgid "usage: gpgm [options] "
msgstr "utilisation: gpgm [options] "
-#: g10/g10.c:417
+#: g10/g10.c:421
msgid "usage: gpg [options] "
msgstr "utilisation: gpg [options] "
-#: g10/g10.c:458
+#: g10/g10.c:462
msgid "conflicting commands\n"
msgstr "commandes en conflit\n"
-#: g10/g10.c:596
+#: g10/g10.c:600
#, c-format
msgid "note: no default option file '%s'\n"
msgstr "note: pas de fichier d'options par défaut '%s'\n"
-#: g10/g10.c:600
+#: g10/g10.c:604
#, c-format
msgid "option file '%s': %s\n"
msgstr "fichier d'options '%s' : %s\n"
-#: g10/g10.c:607
+#: g10/g10.c:611
#, c-format
msgid "reading options from '%s'\n"
msgstr "lire les options de '%s'\n"
-#: g10/g10.c:778 g10/g10.c:790
+#: g10/g10.c:784 g10/g10.c:796
msgid "selected cipher algorithm is invalid\n"
msgstr "l'algorithme de cryptage sélectionné est invalide\n"
-#: g10/g10.c:784 g10/g10.c:796
+#: g10/g10.c:790 g10/g10.c:802
msgid "selected digest algorithm is invalid\n"
msgstr "la fonction de hachage sélectionnée est invalide\n"
-#: g10/g10.c:799
+#: g10/g10.c:805
#, c-format
msgid "compress algorithm must be in range %d..%d\n"
msgstr "l'algorithme de compression doit faire partie de l'échelle %d..%d\n"
-#: g10/g10.c:801
+#: g10/g10.c:807
msgid "completes-needed must be greater than 0\n"
msgstr "le nombre de signatures complètes minimal doit être supérieur à 0\n"
-#: g10/g10.c:803
+#: g10/g10.c:809
msgid "marginals-needed must be greater than 1\n"
msgstr "le nombre de singatures marginales minimal doit être supérieur à 1\n"
-#: g10/g10.c:806
+#: g10/g10.c:812
msgid "note: simple S2K mode (0) is strongly discouraged\n"
msgstr "note: le mode S2K simple (0) est fortement déconseillé\n"
-#: g10/g10.c:810
+#: g10/g10.c:816
msgid "invalid S2K mode; must be 0, 1 or 3\n"
msgstr "mode S2K invalide; doit être 0, 1 ou 3\n"
-#: g10/g10.c:886
+#: g10/g10.c:892
#, c-format
msgid "failed to initialize the TrustDB: %s\n"
msgstr "n'a pas pu initialiser la base de confiance: %s\n"
-#: g10/g10.c:892
+#: g10/g10.c:898
msgid "--store [filename]"
msgstr "--store [nom du fichier]"
-#: g10/g10.c:900
+#: g10/g10.c:906
msgid "--symmetric [filename]"
msgstr "--symmetric [nom du fichier]"
-#: g10/g10.c:908
+#: g10/g10.c:914
msgid "--encrypt [filename]"
msgstr "--encrypt [nom du fichier]"
-#: g10/g10.c:921
+#: g10/g10.c:927
msgid "--sign [filename]"
msgstr "--sign [nom du fichier]"
-#: g10/g10.c:934
+#: g10/g10.c:940
msgid "--sign --encrypt [filename]"
msgstr "--sign --encrypt [nom du fichier]"
-#: g10/g10.c:948
+#: g10/g10.c:954
msgid "--clearsign [filename]"
msgstr "--clearsign [nom du fichier]"
-#: g10/g10.c:960
+#: g10/g10.c:966
msgid "--decrypt [filename]"
msgstr "--decrypt [nom du fichier]"
-#: g10/g10.c:969
+#: g10/g10.c:975
msgid "--edit-key username"
msgstr "--edit-key utilisateur"
-#: g10/g10.c:977
+#: g10/g10.c:983
msgid "--delete-secret-key username"
msgstr "--delete-secret-key utilisateur"
-#: g10/g10.c:980
+#: g10/g10.c:986
msgid "--delete-key username"
msgstr "--delete-key utilisateur"
-#: g10/encode.c:215 g10/g10.c:1003 g10/keylist.c:80
+#: g10/encode.c:216 g10/g10.c:1009
#, c-format
msgid "can't open %s: %s\n"
msgstr "ne peut ouvrir %s: %s\n"
-#: g10/g10.c:1014
+#: g10/g10.c:1020
msgid "-k[v][v][v][c] [userid] [keyring]"
msgstr "-k[v][v][v][c] [utilisateur] [porte-clés]"
-#: g10/g10.c:1069
+#: g10/g10.c:1076
#, c-format
msgid "dearmoring failed: %s\n"
msgstr "suppression d'armure non réussie: %s\n"
-#: g10/g10.c:1077
+#: g10/g10.c:1084
#, c-format
msgid "enarmoring failed: %s\n"
msgstr "mise d'armure non réussie:%s \n"
-#: g10/g10.c:1138
+#: g10/g10.c:1145
#, c-format
msgid "invalid hash algorithm '%s'\n"
msgstr "algorithme de hachage invalide '%s'\n"
-#: g10/g10.c:1220
+#: g10/g10.c:1227
msgid "[filename]"
msgstr "[nom du fichier]"
-#: g10/decrypt.c:59 g10/g10.c:1222 g10/verify.c:66
+#: g10/decrypt.c:59 g10/g10.c:1229 g10/verify.c:66
#, c-format
msgid "can't open '%s'\n"
msgstr "ne peut ouvrir '%s'\n"
-#: g10/g10.c:1269
+#: g10/g10.c:1276
msgid ""
"RSA keys are deprecated; please consider creating a new key and use this key "
"in the future\n"
msgstr ""
"Les clés RSA sont déconseillées: considérez créer une nouvelle clé et "
"l'utiliser dans l'avenir\n"
#: g10/armor.c:341 g10/armor.c:381
msgid "armor header: "
msgstr "entête d'armure: "
#: g10/armor.c:346
msgid "invalid clearsig header\n"
msgstr "entête de signature claire invalide\n"
#: g10/armor.c:372
msgid "invalid armor header: "
msgstr "entête d'armure invalide: "
#: g10/armor.c:449
#, c-format
msgid "armor: %s\n"
msgstr "armure: %s\n"
#: g10/armor.c:493
msgid "invalid dash escaped line: "
msgstr "ligne d'échappement invalide: "
#: g10/armor.c:562
msgid "invalid clear text header: "
msgstr "entête de texte clair invalide: "
#: g10/armor.c:794
#, c-format
msgid "invalid radix64 character %02x skipped\n"
msgstr "caractère %02x invalide en base 64 sauté\n"
#: g10/armor.c:827
msgid "premature eof (no CRC)\n"
msgstr "fin de fichier prématurée (pas de CRC)\n"
#: g10/armor.c:846
msgid "premature eof (in CRC)\n"
msgstr "fin de fichier prématurée (dans le CRC)\n"
#: g10/armor.c:850
msgid "malformed CRC\n"
msgstr "CRC malformé\n"
#: g10/armor.c:854
#, c-format
msgid "CRC error; %06lx - %06lx\n"
msgstr "Erreur de CRC; 06lx - %06lx\n"
#: g10/armor.c:873
msgid "premature eof (in Trailer)\n"
msgstr "fin de fichier prématurée (dans la remorque)\n"
#: g10/armor.c:877
msgid "error in trailer line\n"
msgstr "erreur dans la ligne de remorque\n"
#: g10/armor.c:1131
msgid "no valid RFC1991 or OpenPGP data found.\n"
msgstr "pas de donnée RFC1991 ou OpenPGP valide trouvée.\n"
#: g10/pkclist.c:71
#, c-format
msgid ""
"No owner trust defined for %lu:\n"
"%4u%c/%08lX %s \""
msgstr ""
"Pas de confiance définie pour %lu:\n"
"%4u%c/%08lX %s"
#: g10/pkclist.c:81
msgid ""
"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"
msgstr ""
"Décidez à quel point vous avez confiance en cet utilisateur pour vérifier\n"
"correctement les clés des autres utilisateurs (en regardant des passeports,\n"
"en vérifiant les empreintes de diverses sources...)?\n"
"\n"
" 1 = Je ne sais pas\n"
" 2 = Je ne lui fais pas confiance\n"
" 3 = Je le crois marginalement\n"
" 4 = Je le crois entièrement\n"
" s = montrez moi plus d'informations\n"
#: g10/pkclist.c:90
msgid " m = back to the main menu\n"
msgstr " m = retour au menu principal\n"
#. a string with valid answers
#: g10/pkclist.c:95
msgid "sSmM"
msgstr "sSmM"
#: g10/pkclist.c:99
msgid "edit_ownertrust.value"
msgstr ""
"C'est à vous d'assigner une valeur ici; cette valeur ne sera jamais\n"
"exportée à une tierce personne. Nous en avons besoin pour créer le;\n"
"réseau de confiance (web-of-trust); cela n'a rien à voir avec le\n"
"réseau de certificats (créé implicitement)"
#: g10/pkclist.c:99
msgid "Your decision? "
msgstr "Votre décision? "
#: g10/pkclist.c:118
msgid "You will see a list of signators etc. here\n"
msgstr "Vous verrez une liste de signataires etc. ici\n"
#: g10/pkclist.c:145
msgid ""
"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"
msgstr ""
"N'a pas pu trouver un chemin de confiance valide jusqu'à la clé. Voyons si\n"
"nous ne pouvons pas assigner quelques valeurs de confiance.\n"
#: g10/pkclist.c:168
msgid ""
"No owner trust values changed.\n"
"\n"
msgstr ""
"Pas de valeur de confiance changée.\n"
"\n"
#: g10/pkclist.c:188
msgid "revoked_key.override"
msgstr ""
"Si vous voulez utiliser cette clé révoquée quand-même, répondez \"oui\"."
#: g10/pkclist.c:189 g10/pkclist.c:279
msgid "Use this key anyway? "
msgstr "Utiliser cette clé quand-même? "
#: g10/pkclist.c:274
msgid ""
"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"
msgstr ""
"Il n'est pas certain que la clé appartient à sos propriétaire.\n"
"Si vous savez *vraiment* ce que vous faites, vous pouvez répondre\n"
"oui à la prochaine question\n"
"\n"
#: g10/pkclist.c:278
msgid "untrusted_key.override"
msgstr ""
"Si vous voulez utiliser cette clé peu sûre quand-même, répondez \"oui\"."
#: g10/pkclist.c:283
msgid "WARNING: Using untrusted key!\n"
msgstr "ATTENTION: Utilisation d'une clé sans confiance!\n"
#: g10/pkclist.c:319
msgid "WARNING: This key has been revoked by its owner!\n"
msgstr "ATTENTION: Cette clé à été révoquée par son propriétaire\n"
#: g10/pkclist.c:320
msgid " This could mean that the signature is forgery.\n"
msgstr " Cela pourrait signifier que la signature est fausse.\n"
#: g10/pkclist.c:341
msgid "Note: This key has expired!\n"
msgstr "Note: Cette clé a expiré!\n"
#: g10/pkclist.c:348
msgid "WARNING: This key is not certified with a trusted signature!\n"
msgstr ""
"ATTENTION: Cette clé n'est pas certifiée avec une signature de confiance!\n"
#: g10/pkclist.c:350
msgid ""
" There is no indication that the signature belongs to the owner.\n"
msgstr " Rien ne dit que la signature appartient au propriétaire.\n"
#: g10/pkclist.c:365
msgid "WARNING: We do NOT trust this key!\n"
msgstr "ATTENTION: On ne fait PAS confiance à cette clé\n"
#: g10/pkclist.c:366
msgid " The signature is probably a FORGERY.\n"
msgstr " La signature est certainement FAUSSE.\n"
#: g10/pkclist.c:373
msgid ""
"WARNING: This key is not certified with sufficiently trusted signatures!\n"
msgstr ""
"ATTENTION: Les signatures de cette clé n'ont pas une confiance suffisante!\n"
#: g10/pkclist.c:376
msgid " It is not certain that the signature belongs to the owner.\n"
msgstr ""
" Il n'est pas sûr que la signature appartient au propriétaire.\n"
#: g10/pkclist.c:421
msgid ""
"You did not specify a user ID. (you may use \"-r\")\n"
"\n"
msgstr ""
"Vous n'avez pas spécifié un nom d'utilisateur. (vous pouvez utiliser "
"\"-r\")\n"
#: g10/pkclist.c:425
msgid "pklist.user_id.enter"
msgstr "Entrez le nom de l'adresse à qui vous voulez envoyer le message."
#: g10/pkclist.c:426
msgid "Enter the user ID: "
msgstr "Entrez le nom d'utilisateur: "
#: g10/pkclist.c:437
msgid "No such user ID.\n"
msgstr "Pas de tel utilisateur.\n"
#: g10/pkclist.c:471 g10/pkclist.c:498
#, c-format
msgid "%s: skipped: %s\n"
msgstr "%s: sauté: %s\n"
#: g10/pkclist.c:479
#, c-format
msgid "%s: error checking key: %s\n"
msgstr "%s: erreur pendant la vérification de la clé: %s\n"
#: g10/pkclist.c:505
msgid "no valid addressees\n"
msgstr "pas de destinataire valide\n"
# g10/keygen.c:123 ???
#: g10/keygen.c:123
msgid "writing self signature\n"
msgstr "écriture de l'auto-signature\n"
# g10/keygen.c:161 ???
#: g10/keygen.c:161
msgid "writing key binding signature\n"
msgstr "écriture de la signature de \"key-binding\"\n"
#: g10/keygen.c:387
msgid "Please select what kind of key you want:\n"
msgstr "Sélectionnez le type de clé désiré:\n"
#: g10/keygen.c:389
#, c-format
msgid " (%d) DSA and ElGamal (default)\n"
msgstr " (%d) DSA et ElGamal (défaut)\n"
#: g10/keygen.c:390
#, c-format
msgid " (%d) ElGamal (sign and encrypt)\n"
msgstr " (%d) ElGamal (signature et cryptage)\n"
#: g10/keygen.c:392
#, c-format
msgid " (%d) ElGamal (encrypt only)\n"
msgstr " (%d) ElGamal (cryptage seulement)\n"
#: g10/keygen.c:393
#, c-format
msgid " (%d) DSA (sign only)\n"
msgstr " (%d) DSA (signature seulement)\n"
#: g10/keygen.c:394
#, c-format
msgid " (%d) ElGamal in a v3 packet\n"
msgstr " (%d) ElGamal dans un paquet v3\n"
#: g10/keygen.c:398
msgid "keygen.algo"
msgstr "Sélectionnez l'algorithme à utiliser:"
#: g10/keygen.c:398
msgid "Your selection? "
msgstr "Votre choix? "
#: g10/keygen.c:424
msgid "Invalid selection.\n"
msgstr "Choix invalide.\n"
#: g10/keygen.c:436
#, c-format
msgid ""
"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"
msgstr ""
"Préparation à la génération d'une nouvelle paire de clés %s.\n"
" la taille minimale est 768 bits\n"
" la taille par défaut est 1024 bits\n"
" la taille maximale conseillée est 2048 bits\n"
#: g10/keygen.c:442
msgid "keygen.size"
msgstr ""
#: g10/keygen.c:443
msgid "What keysize do you want? (1024) "
msgstr "Quelle taille de clé désirez-vous? (1024) "
#: g10/keygen.c:448
msgid "DSA only allows keysizes from 512 to 1024\n"
msgstr "DSA permet seulement des tailles comprises entre 512 et 1024\n"
#: g10/keygen.c:450
msgid "keysize too small; 768 is smallest value allowed.\n"
msgstr "taille trop petite; 768 est la plus petite valeur permise.\n"
#: g10/keygen.c:453
msgid ""
"Keysizes larger than 2048 are not suggested because\n"
"computations take REALLY long!\n"
msgstr ""
"Les tailles supérieures à 2048 ne sont pas conseillées car\n"
"les calculs sont VRAIMENT longs!\n"
#: g10/keygen.c:455
msgid "keygen.size.huge.okay"
msgstr ""
#: g10/keygen.c:456
msgid "Are you sure that you want this keysize? "
msgstr "Etes-vous sûr que vous voulez cette taille? "
#: g10/keygen.c:457
msgid ""
"Okay, but keep in mind that your monitor and keyboard radiation is also very "
"vulnerable to attacks!\n"
msgstr ""
"D'accord, mais n'oubliez pas que votre écran et les radiations du clavier "
"sont aussi très vulnérables aux attaques!\n"
#: g10/keygen.c:464
msgid "keygen.size.large.okay"
msgstr ""
#: g10/keygen.c:465
msgid "Do you really need such a large keysize? "
msgstr "Avez-vous réellement besoin d'une taille aussi grande? "
#: g10/keygen.c:471
#, c-format
msgid "Requested keysize is %u bits\n"
msgstr "La taille demandée est %u bits\n"
#: g10/keygen.c:474 g10/keygen.c:478
#, c-format
msgid "rounded up to %u bits\n"
msgstr "arrondie à %u bits\n"
#: g10/keygen.c:491
msgid ""
"Please specify how long the key should be valid.\n"
" 0 = key does not expire\n"
" <n> = key expires in n days\n"
" <n>w = key expires in n weeks\n"
" <n>m = key expires in n months\n"
" <n>y = key expires in n years\n"
msgstr ""
"Spécifiez combien de temps cette clé devrait être valide.\n"
" 0 = la clé n'expire pas\n"
" <n> = la clé expire dans n jours\n"
" <n>w = la clé expire dans n semaines\n"
" <n>m = la clé expire dans n mois\n"
" <n>y = la clé expire dans n ans\n"
#: g10/keygen.c:506
msgid "keygen.valid"
msgstr ""
#: g10/keygen.c:506
msgid "Key is valid for? (0) "
msgstr "La clé est valide pour? (0) "
#: g10/keygen.c:517
msgid "invalid value\n"
msgstr "valeur invalide\n"
#: g10/keygen.c:522
msgid "Key does not expire at all\n"
msgstr "La clé n'expire pas du tout\n"
#. print the date when the key expires
#: g10/keygen.c:528
#, c-format
msgid "Key expires at %s\n"
msgstr "La clé expire le %s\n"
#: g10/keygen.c:532
msgid "keygen.valid.okay"
msgstr ""
#: g10/keygen.c:533
msgid "Is this correct (y/n)? "
msgstr "Est-ce correct (o/n)? "
-#: g10/keygen.c:561
+#: g10/keygen.c:569
msgid ""
"\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) <heinrichh@duesseldorf.de>\"\n"
"\n"
msgstr ""
"\n"
"Vous avez besoin d'un nom d'utilisateur pour identifier votre clé; le "
"programme le construit à partir du nom réel, d'un commentaire et d'une\n"
"adresse e-mail de cette manière:\n"
" \"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de\"\n"
"\n"
-#: g10/keygen.c:572
+#: g10/keygen.c:580
msgid "keygen.name"
msgstr ""
-#: g10/keygen.c:572
+#: g10/keygen.c:580
msgid "Real name: "
msgstr "Nom: "
-#: g10/keygen.c:576
+#: g10/keygen.c:584
msgid "Invalid character in name\n"
msgstr "Caractère invalide dans le nom\n"
-#: g10/keygen.c:578
+#: g10/keygen.c:586
msgid "Name may not start with a digit\n"
msgstr "Le nom ne doit pas commencer avec un chiffre\n"
-#: g10/keygen.c:580
+#: g10/keygen.c:588
msgid "Name must be at least 5 characters long\n"
msgstr "Le nom doit faire au moins 5 caractères de long\n"
-#: g10/keygen.c:588
+#: g10/keygen.c:596
msgid "keygen.email"
msgstr ""
-#: g10/keygen.c:588
+#: g10/keygen.c:596
msgid "Email address: "
msgstr "Adresse e-mail: "
-#: g10/keygen.c:600
+#: g10/keygen.c:608
msgid "Not a valid email address\n"
msgstr "Ce n'est pas une adresse e-mail valide\n"
-#: g10/keygen.c:608
+#: g10/keygen.c:616
msgid "keygen.comment"
msgstr ""
-#: g10/keygen.c:608
+#: g10/keygen.c:616
msgid "Comment: "
msgstr "Commentaire: "
-#: g10/keygen.c:614
+#: g10/keygen.c:622
msgid "Invalid character in comment\n"
msgstr "Caractère invalide dans le commentaire\n"
-#: g10/keygen.c:634
+#: g10/keygen.c:642
#, c-format
msgid ""
"You selected this USER-ID:\n"
" \"%s\"\n"
"\n"
msgstr ""
"Vous avez sélectionné ce nom d'utilisateur:\n"
" \"%s\"\n"
"\n"
-#: g10/keygen.c:637
+#: g10/keygen.c:645
msgid "NnCcEeOoQq"
msgstr "NnCcEeOoQq"
-#: g10/keygen.c:646
+#: g10/keygen.c:654
msgid "keygen.userid.cmd"
msgstr ""
"N pour changer le nom.\n"
"C pour changer le commentaire.\n"
"E pour changer l'adresse e-mail.\n"
"O pour continuer à générer la clé.\n"
"Q pour arrêter de générer de clé."
-#: g10/keygen.c:647
+#: g10/keygen.c:655
msgid "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "
msgstr "Changer le (N)om, le (C)ommentaire, l'(E)-mail ou (O)K/(Q)uitter? "
-#: g10/keygen.c:694
+#: g10/keygen.c:702
msgid ""
"You need a Passphrase to protect your secret key.\n"
"\n"
msgstr ""
"Vous avez besoin d'un mot de passe pour protéger votre clé secrète.\n"
"\n"
-#: g10/keyedit.c:389 g10/keygen.c:702
+#: g10/keyedit.c:389 g10/keygen.c:710
msgid "passphrase not correctly repeated; try again.\n"
msgstr "le mot de passe n'a pas été correctement répété; recommencez.\n"
-#: g10/keygen.c:708
+#: g10/keygen.c:716
msgid ""
"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"
msgstr ""
"Vous ne voulez pas de mot de passe - c'est sûrement une *mauvaise* idée!\n"
"Je l'accepte quand-même. Vous pouvez changer votre mot de passe quand vous\n"
"le désirez, en utilisant ce programme avec l'option \"--edit-key\".\n"
"\n"
-#: g10/keygen.c:729
+#: g10/keygen.c:737
msgid ""
"We need to generate a lot of random bytes. It is a good idea to perform\n"
"some other action (work in another window, move the mouse, utilize the\n"
"network and the disks) during the prime generation; this gives the random\n"
"number generator a better chance to gain enough entropy.\n"
msgstr ""
"Beaucoup d'octets aléatoires doivent être générés. Vous devriez\n"
"faire autre chose (travailler dans une autre fenêtre, bouger la souris,\n"
"utiliser le réseau et les disques) pendant la génération de nombres\n"
"premiers; cela permet au générateur de nombres aléatoires de gagner assez\n"
"d'entropie plus facilement.\n"
-#: g10/keygen.c:799
+#: g10/keygen.c:807
msgid "Key generation can only be used in interactive mode\n"
msgstr "La génération de clé ne peut être faite qu'en mode interactif\n"
-#: g10/keygen.c:807
+#: g10/keygen.c:815
msgid "DSA keypair will have 1024 bits.\n"
msgstr "La paire de clés DSA fera 1024 bits.\n"
-#: g10/keygen.c:813
+#: g10/keygen.c:821
msgid "Key generation cancelled.\n"
msgstr "La génération a été annulée.\n"
-#: g10/keygen.c:823
+#: g10/keygen.c:831
#, c-format
msgid "writing public certificate to '%s'\n"
msgstr "écriture d'un certificat public à '%s'\n"
-#: g10/keygen.c:824
+#: g10/keygen.c:832
#, c-format
msgid "writing secret certificate to '%s'\n"
msgstr "écriture d'un certificat secret à '%s'\n"
-#: g10/keygen.c:901
+#: g10/keygen.c:909
msgid "public and secret key created and signed.\n"
msgstr "les clés publique et secrète ont été créées et signées.\n"
-#: g10/keygen.c:903
+#: g10/keygen.c:911
msgid ""
"Note that this key cannot be used for encryption. You may want to use\n"
"the command \"--add-key\" to generate a secondary key for this purpose.\n"
msgstr ""
"Notez que cette clé ne peut être utilisée pour crypter. Vous pouvez "
"utiliser\n"
"la commande \"--add-key\" pour générer une clé secondaire à cette fin.\n"
-#: g10/keygen.c:917 g10/keygen.c:1002
+#: g10/keygen.c:925 g10/keygen.c:1010
#, c-format
msgid "Key generation failed: %s\n"
msgstr "La génération de clé a échoué: %s\n"
-#: g10/keygen.c:979
+#: g10/keygen.c:987
msgid "keygen.sub.okay"
msgstr "Répondez \"oui\" (ou simplement \"o\") pour générer la sous-clé"
-#: g10/keygen.c:980
+#: g10/keygen.c:988
msgid "Really create? "
msgstr "Créer vraiment? "
-#: g10/encode.c:88
+#: g10/encode.c:88 g10/openfile.c:81 g10/openfile.c:158
#, c-format
msgid "%s: can't open: %s\n"
msgstr "%s: ne peut ouvrir: %s\n"
#: g10/encode.c:107
#, c-format
msgid "error creating passphrase: %s\n"
msgstr "erreur pendant la création du mot de passe: %s\n"
-#: g10/encode.c:154 g10/encode.c:267
+#: g10/encode.c:155 g10/encode.c:269
#, c-format
msgid "%s: warning: empty file\n"
msgstr "%s: attention: fichier vide\n"
-#: g10/encode.c:221
+#: g10/encode.c:222
#, c-format
msgid "reading from '%s'\n"
msgstr "lecture de '%s'\n"
-#: g10/encode.c:395
+#: g10/encode.c:397
#, c-format
msgid "%s encrypted for: %s\n"
msgstr "%s crypté pour: %s\n"
-#: g10/getkey.c:854
+#: g10/getkey.c:850
#, c-format
msgid "using secondary key %08lX instead of primary key %08lX\n"
msgstr ""
"utilisation de la clé secondaire %08lX à la place de la clé\n"
"principale %08lX\n"
-#: g10/import.c:106 g10/trustdb.c:1100
+#: g10/import.c:125 g10/trustdb.c:1082
#, c-format
msgid "can't open file: %s\n"
msgstr "ne peut ouvrir le fichier: %s\n"
-#: g10/import.c:122
+#: g10/import.c:141
#, c-format
msgid "skipping block of type %d\n"
msgstr "ne prend pas en compte le bloc du type %d\n"
-#: g10/import.c:132 g10/trustdb.c:1175
+#: g10/import.c:148 g10/trustdb.c:1284 g10/trustdb.c:1349
+#, c-format
+msgid "%lu keys so far processed\n"
+msgstr "%lu clés traitées jusqu'ici\n"
+
+#: g10/import.c:153 g10/trustdb.c:1157
#, c-format
msgid "read error: %s\n"
msgstr "erreur de lecture: %s\n"
-#: g10/import.c:273 g10/import.c:446
+#: g10/import.c:155
+#, c-format
+msgid "Total number processed: %lu\n"
+msgstr " Quantité totale traitée: %lu\n"
+
+#: g10/import.c:157
+#, c-format
+msgid " w/o user IDs: %lu\n"
+msgstr " sans nom d'utilisateur: %lu\n"
+
+#: g10/import.c:159
+#, c-format
+msgid " imported: %lu"
+msgstr " importé: %lu"
+
+#: g10/import.c:165
+#, c-format
+msgid " unchanged: %lu\n"
+msgstr " inchangé: %lu\n"
+
+#: g10/import.c:167
+#, c-format
+msgid " new user IDs: %lu\n"
+msgstr " nouveaux noms d'utilisateur: %lu\n"
+
+#: g10/import.c:169
+#, c-format
+msgid " new subkeys: %lu\n"
+msgstr " nouvelles sous-clés: %lu\n"
+
+#: g10/import.c:171
+#, c-format
+msgid " new signatures: %lu\n"
+msgstr " nouvelles signatures: %lu\n"
+
+#: g10/import.c:173
+#, c-format
+msgid " new key revocations: %lu\n"
+msgstr " nouvelles révocations de clés: %lu\n"
+
+#: g10/import.c:175
+#, c-format
+msgid " secret keys read: %lu\n"
+msgstr " clés secrètes lues: %lu\n"
+
+#: g10/import.c:177
+#, c-format
+msgid " secret keys imported: %lu\n"
+msgstr " clés secrètes importées: %lu\n"
+
+#: g10/import.c:179
+#, c-format
+msgid " secret keys unchanged: %lu\n"
+msgstr " clés secrètes inchangées: %lu\n"
+
+#: g10/import.c:321 g10/import.c:513
#, c-format
msgid "key %08lX: no user id\n"
msgstr "clé %08lX: pas de nom d'utilisateur\n"
-#: g10/import.c:283
+#: g10/import.c:332
#, c-format
msgid "key %08lX: no valid user ids\n"
msgstr "clé %08lX: pas de nom d'utilisateur valide\n"
-#: g10/import.c:285
+#: g10/import.c:334
msgid "this may be caused by a missing self-signature\n"
msgstr "cela peut provenir d'une auto-signature manquante\n"
-#: g10/import.c:294 g10/import.c:512
+#: g10/import.c:345 g10/import.c:581
#, c-format
msgid "key %08lX: public key not found: %s\n"
msgstr "clé %08lX: clé publique pas trouvée: %s\n"
-#: g10/import.c:300
+#: g10/import.c:351
msgid "no default public keyring\n"
msgstr "pas de porte-clés public par défaut\n"
-#: g10/import.c:304
+#: g10/import.c:355 g10/openfile.c:105
#, c-format
msgid "writing to '%s'\n"
msgstr "écriture de '%s'\n"
-#: g10/import.c:308 g10/import.c:363 g10/import.c:566
+#: g10/import.c:359 g10/import.c:419 g10/import.c:635
#, c-format
msgid "can't lock public keyring: %s\n"
msgstr "ne peut verrouiller le porte-clés public: %s\n"
-#: g10/import.c:311
+#: g10/import.c:362
#, c-format
msgid "can't write to keyring: %s\n"
msgstr "ne peut écrire sur le porte-clés: %s\n"
-#. we are ready
-#: g10/import.c:314
+#: g10/import.c:366
#, c-format
msgid "key %08lX: public key imported\n"
msgstr "clé %08lX: clé publique importée\n"
-#: g10/import.c:323
+#: g10/import.c:379
#, c-format
msgid "key %08lX: doesn't match our copy\n"
msgstr "clé %08lX: ne vérifie pas notre copie\n"
-#: g10/import.c:336 g10/import.c:521
+#: g10/import.c:392 g10/import.c:590
#, c-format
msgid "key %08lX: can't locate original keyblock: %s\n"
msgstr "clé %08lX: ne peut trouver le bloc de clés original: %s\n"
-#: g10/import.c:343 g10/import.c:528
+#: g10/import.c:399 g10/import.c:597
#, c-format
msgid "key %08lX: can't read original keyblock: %s\n"
msgstr "clé %08lX: ne peut lire le bloc de clés original: %s\n"
-#: g10/import.c:360 g10/import.c:461 g10/import.c:563
+#: g10/import.c:416 g10/import.c:528 g10/import.c:632
msgid "writing keyblock\n"
msgstr "écriture du bloc de clés\n"
-#: g10/import.c:366 g10/import.c:569
+#: g10/import.c:422 g10/import.c:638
#, c-format
msgid "can't write keyblock: %s\n"
msgstr "ne peut écrire le bloc de clés: %s\n"
-#: g10/import.c:370
+#: g10/import.c:427
#, c-format
msgid "key %08lX: 1 new user-id\n"
msgstr "clé %08lX: un nouvel utilisateur\n"
-#: g10/import.c:373
+#: g10/import.c:430
#, c-format
msgid "key %08lX: %d new user-ids\n"
msgstr "clé %08lX: %d nouveaux utilisateurs\n"
-#: g10/import.c:376
+#: g10/import.c:433
#, c-format
msgid "key %08lX: 1 new signature\n"
msgstr "clé %08lX: une nouvelle signature\n"
-#: g10/import.c:379
+#: g10/import.c:436
#, c-format
msgid "key %08lX: %d new signatures\n"
msgstr "clé %08lX: %d nouvelles signatures\n"
-#: g10/import.c:382
+#: g10/import.c:439
#, c-format
msgid "key %08lX: 1 new subkey\n"
msgstr "clé %08lX: une nouvelle sous-clé\n"
-#: g10/import.c:385
+#: g10/import.c:442
#, c-format
msgid "key %08lX: %d new subkeys\n"
msgstr "clé %08lX: %d nouvelles sous-clés\n"
-#: g10/import.c:389
+#: g10/import.c:452
#, c-format
msgid "key %08lX: not changed\n"
msgstr "clé %08lX: n'a pas changé\n"
-#: g10/import.c:464
+#: g10/import.c:531
#, c-format
msgid "can't lock secret keyring: %s\n"
msgstr "ne peut verrouiller le porte-clés secret: %s\n"
-#: g10/import.c:467
+#: g10/import.c:534
#, c-format
msgid "can't write keyring: %s\n"
msgstr "ne peut écrire le porte-clés: %s\n"
#. we are ready
-#: g10/import.c:470
+#: g10/import.c:537
#, c-format
msgid "key %08lX: secret key imported\n"
msgstr "clé %08lX: clé secrète importée\n"
#. we can't merge secret keys
-#: g10/import.c:473
+#: g10/import.c:541
#, c-format
msgid "key %08lX: already in secret keyring\n"
msgstr "clé %08lX: déjà dans le porte-clés secret\n"
-#: g10/import.c:477
+#: g10/import.c:546
#, c-format
msgid "key %08lX: secret key not found: %s\n"
msgstr "clé %08lX: clé secrète pas trouvée: %s\n"
-#: g10/import.c:506
+#: g10/import.c:575
#, c-format
msgid "key %08lX: no public key - can't apply revocation certificate\n"
msgstr ""
"clé %08lX: pas de clé publique - ne peut appliquer le certificat de\n"
"révocation\n"
-#: g10/import.c:539
+#: g10/import.c:608
#, c-format
msgid "key %08lX: invalid revocation certificate: %s - rejected\n"
msgstr "clé %08lX: certificat de révocation invalide: %s - rejeté\n"
-#. we are ready
-#: g10/import.c:572
+#: g10/import.c:642
#, c-format
msgid "key %08lX: revocation certificate imported\n"
msgstr "clé %08lX: certificat de révocation importé\n"
-#: g10/import.c:602
+#: g10/import.c:673
#, c-format
msgid "key %08lX: no user-id for signature\n"
msgstr "clé %08lX: pas d'utilisateur pour la signature\n"
-#: g10/import.c:609
+#: g10/import.c:680
#, c-format
msgid "key %08lX: unsupported public key algorithm\n"
msgstr "clé %08lX: algorithme de clé publique non supporté\n"
-#: g10/import.c:610
+#: g10/import.c:681
#, c-format
msgid "key %08lX: invalid self-signature\n"
msgstr "clé %08lX: auto-signature invalide\n"
-#: g10/import.c:639
+#: g10/import.c:710
#, c-format
msgid "key %08lX: skipped userid '"
msgstr "clé %08lX: utilisateur non pris en compte '"
-#: g10/import.c:662
+#: g10/import.c:733
#, c-format
msgid "key %08lX: revocation certificate at wrong place - skipped\n"
msgstr ""
"clé %08lX: certificat de révocation au mauvais endroit - non prise en "
"compte\n"
-#: g10/import.c:670
+#: g10/import.c:741
#, c-format
msgid "key %08lX: invalid revocation certificate: %s - skipped\n"
msgstr ""
"clé %08lX: certificat de révocation invalide: %s - non prise en compte\n"
-#: g10/import.c:732
+#: g10/import.c:803
#, c-format
msgid "key %08lX: revocation certificate added\n"
msgstr "clé %08lX: certificat de révocation ajouté\n"
-#: g10/import.c:795 g10/import.c:831
+#: g10/import.c:866 g10/import.c:902
#, c-format
msgid "key %08lX: our copy has no self-signature\n"
msgstr "clé %08lX: notre copie n'a pas d'auto-signature\n"
#: g10/keyedit.c:81
#, c-format
msgid "%s: user not found\n"
msgstr "%s: utilisateur non trouvé\n"
#: g10/keyedit.c:164
msgid "[self-signature]"
msgstr "[auto-signature]"
#: g10/keyedit.c:182
msgid "1 bad signature\n"
msgstr "une mauvaise signature\n"
#: g10/keyedit.c:184
#, c-format
msgid "%d bad signatures\n"
msgstr "%d mauvaises signatures\n"
#: g10/keyedit.c:186
msgid "1 signature not checked due to a missing key\n"
msgstr "une signature non vérifiée à cause d'une clé manquante\n"
#: g10/keyedit.c:188
#, c-format
msgid "%d signatures not checked due to missing keys\n"
msgstr "%d signatures non vérifiées à cause de clés manquantes\n"
#: g10/keyedit.c:190
msgid "1 signature not checked due to an error\n"
msgstr "une signature non vérifiée à cause d'une erreur\n"
#: g10/keyedit.c:192
#, c-format
msgid "%d signatures not checked due to errors\n"
msgstr "%d signatures non vérifiées à cause d'erreurs\n"
#: g10/keyedit.c:194
msgid "1 user id without valid self-signature detected\n"
msgstr "un nom d'utilisateur sans auto-signature valide détecté\n"
#: g10/keyedit.c:196
#, c-format
msgid "%d user ids without valid self-signatures detected\n"
msgstr "%d nom d'utilisateurs sans auto-signature valide détecté\n"
#: g10/keyedit.c:258
#, c-format
msgid "Already signed by key %08lX\n"
msgstr "Déjà signé par la clé %08lX\n"
#: g10/keyedit.c:266
#, c-format
msgid "Nothing to sign with key %08lX\n"
msgstr "Rien à signer avec la clé %08lX\n"
#: g10/keyedit.c:275
msgid ""
"Are you really sure that you want to sign this key\n"
"with your key: \""
msgstr ""
"Etes-vous vraiment sûr(e) que vous voulez signer cette clé\n"
"avec votre clé: \""
#: g10/keyedit.c:282
msgid "sign_uid.okay"
msgstr ""
#: g10/keyedit.c:282
msgid "Really sign? "
msgstr "Signer réellement? "
#: g10/keyedit.c:303
#, c-format
msgid "signing failed: %s\n"
msgstr "la signature a échoué: %s\n"
#: g10/keyedit.c:356
msgid "This key is not protected.\n"
msgstr "Cette clé n'est pas protégée.\n"
#: g10/keyedit.c:359
msgid "Key is protected.\n"
msgstr "La clé est protégée.\n"
#: g10/keyedit.c:376
#, c-format
msgid "Can't edit this key: %s\n"
msgstr "Ne peut éditer cette clé: %s\n"
#: g10/keyedit.c:381
msgid ""
"Enter the new passphrase for this secret key.\n"
"\n"
msgstr "Entrez le nouveau mot de passe pour cette clé secrète.\n"
#: g10/keyedit.c:393
msgid ""
"You don't want a passphrase - this is probably a *bad* idea!\n"
"\n"
msgstr ""
"Vous ne voulez pas de mot de passe - cela est certainement une *mauvaise* "
"idée\n"
#: g10/keyedit.c:395
msgid "change_passwd.empty.okay"
msgstr ""
#: g10/keyedit.c:396
msgid "Do you really want to do this? "
msgstr "Voulez-vous vraiment faire cela? "
#: g10/keyedit.c:489
msgid "quit"
msgstr "quitter"
#: g10/keyedit.c:489
msgid "quit this menu"
msgstr "quitter ce menu"
#: g10/keyedit.c:490
msgid "q"
msgstr "q"
#: g10/keyedit.c:491
msgid "save"
msgstr "enregistrer"
#: g10/keyedit.c:491
msgid "save and quit"
msgstr "enregistrer et quitter"
#: g10/keyedit.c:492
msgid "help"
msgstr "aide"
#: g10/keyedit.c:492
msgid "show this help"
msgstr "montrer cette aide"
# g10/keyedit.c:556 ???
#: g10/keyedit.c:494
msgid "fpr"
msgstr "fpr"
#: g10/keyedit.c:494
msgid "show fingerprint"
msgstr "montrer l'empreinte"
#: g10/keyedit.c:495
msgid "list"
msgstr "lister"
#: g10/keyedit.c:495
msgid "list key and user ids"
msgstr "lister la clé et les noms d'utilisateurs"
#: g10/keyedit.c:496
msgid "l"
msgstr "l"
#: g10/keyedit.c:497
msgid "uid"
msgstr "uid"
#: g10/keyedit.c:497
msgid "select user id N"
msgstr "sélectionner le nom d'utilisateur N"
#: g10/keyedit.c:498
msgid "key"
msgstr "clé"
#: g10/keyedit.c:498
msgid "select secondary key N"
msgstr "sélectionner la clé secondaire N"
#: g10/keyedit.c:499
msgid "check"
msgstr "vérifier"
#: g10/keyedit.c:499
msgid "list signatures"
msgstr "lister les signatures"
#: g10/keyedit.c:500
msgid "c"
msgstr "c"
#: g10/keyedit.c:501
msgid "sign"
msgstr "signer"
#: g10/keyedit.c:501
msgid "sign the key"
msgstr "signer la clé"
#: g10/keyedit.c:502
msgid "s"
msgstr "s"
#: g10/keyedit.c:503
msgid "debug"
msgstr "déboguer"
#: g10/keyedit.c:504
msgid "adduid"
msgstr "aj.ut"
#: g10/keyedit.c:504
msgid "add a user id"
msgstr "ajouter un utilisateur"
#: g10/keyedit.c:505
msgid "deluid"
msgstr "suppr.ut"
#: g10/keyedit.c:505
msgid "delete user id"
msgstr "enlever un utilisateur"
#: g10/keyedit.c:506
msgid "addkey"
msgstr "aj.clé"
#: g10/keyedit.c:506
msgid "add a secondary key"
msgstr "ajouter une clé secondaire"
#: g10/keyedit.c:507
msgid "delkey"
msgstr "suppr.clé"
#: g10/keyedit.c:507
msgid "delete a secondary key"
msgstr "enlever une clé secondaire"
#: g10/keyedit.c:508
-#, fuzzy
msgid "expire"
-msgstr "préf"
+msgstr "expire"
#: g10/keyedit.c:508
-#, fuzzy
msgid "change the expire date"
-msgstr "changer le mot de passe"
+msgstr "changer la date d'expiration"
#: g10/keyedit.c:509
msgid "toggle"
msgstr "changer"
#: g10/keyedit.c:509
msgid "toggle between secret and public key listing"
msgstr "passer de la liste des clés secrètes aux clés privées et inversement"
#: g10/keyedit.c:511
msgid "t"
msgstr "t"
#: g10/keyedit.c:512
msgid "pref"
msgstr "préf"
#: g10/keyedit.c:512
msgid "list preferences"
msgstr "lister les préférences"
#: g10/keyedit.c:513
msgid "passwd"
msgstr "mot.pas"
#: g10/keyedit.c:513
msgid "change the passphrase"
msgstr "changer le mot de passe"
#: g10/keyedit.c:514
msgid "trust"
msgstr "confi."
#: g10/keyedit.c:514
msgid "change the ownertrust"
msgstr "changer la confiance"
#: g10/keyedit.c:532
msgid "can't do that in batchmode\n"
msgstr "ne peut faire cela en mode automatique\n"
#. check that they match
#. FIXME: check that they both match
#: g10/keyedit.c:559
msgid "Secret key is available.\n"
msgstr "La clé secrète est disponible.\n"
-#: g10/keyedit.c:575
+#: g10/keyedit.c:576
msgid "keyedit.cmd"
msgstr "Entrez \"aide\" s'il vous plaît."
-#: g10/keyedit.c:575
+#: g10/keyedit.c:576
msgid "Command> "
msgstr "Commande> "
-#: g10/keyedit.c:600
+#: g10/keyedit.c:602
msgid "Need the secret key to to this.\n"
msgstr "Il faut la clé secrète pour faire cela.\n"
-#: g10/keyedit.c:619
+#: g10/keyedit.c:621
msgid "keyedit.save.okay"
msgstr ""
-#: g10/keyedit.c:620
+#: g10/keyedit.c:622
msgid "Save changes? "
msgstr "Enregistrer les changements? "
-#: g10/keyedit.c:622
+#: g10/keyedit.c:624
msgid "keyedit.cancel.okay"
msgstr ""
-#: g10/keyedit.c:623
+#: g10/keyedit.c:625
msgid "Quit without saving? "
msgstr "Quitter sans enregistrer? "
-#: g10/keyedit.c:633
+#: g10/keyedit.c:635
#, c-format
msgid "update failed: %s\n"
msgstr "la mise à jour a échoué: %s\n"
-#: g10/keyedit.c:640
+#: g10/keyedit.c:642
#, c-format
msgid "update secret failed: %s\n"
msgstr "la mise à jour de la clé secrète a échoué: %s\n"
-#: g10/keyedit.c:647
+#: g10/keyedit.c:649
msgid "Key not changed so no update needed.\n"
msgstr "La clé n'a pas changé donc la mise à jour est inutile.\n"
-#: g10/keyedit.c:650 g10/keyedit.c:708
+#: g10/keyedit.c:652 g10/keyedit.c:710
#, c-format
msgid "update of trust db failed: %s\n"
msgstr "la mise à jour de la base de confiance a échoué: %s\n"
-#: g10/keyedit.c:681
+#: g10/keyedit.c:683
msgid "keyedit.sign_all.okay"
msgstr ""
-#: g10/keyedit.c:682
+#: g10/keyedit.c:684
msgid "Really sign all user ids? "
msgstr "Signer vraiment tous les utilisateurs? "
-#: g10/keyedit.c:683
+#: g10/keyedit.c:685
msgid "Hint: Select the user ids to sign\n"
msgstr "Aide: Sélectionner les utilisateurs à signer\n"
-#: g10/keyedit.c:719
+#: g10/keyedit.c:721
msgid "You must select at least one user id.\n"
msgstr "Vous devez sélectionner au moins un utilisateur.\n"
-#: g10/keyedit.c:721
+#: g10/keyedit.c:723
msgid "You can't delete the last user id!\n"
msgstr "Vous ne pouvez pas supprimer le dernier utilisateur!\n"
-#: g10/keyedit.c:723
+#: g10/keyedit.c:725
msgid "keyedit.remove.uid.okay"
msgstr ""
-#: g10/keyedit.c:724
+#: g10/keyedit.c:726
msgid "Really remove all selected user ids? "
msgstr "Enlever réellement tous les utilisateurs sélectionnés? "
-#: g10/keyedit.c:725
+#: g10/keyedit.c:727
msgid "Really remove this user id? "
msgstr "Enlever réellement cet utilisateur? "
-#: g10/keyedit.c:748
+#: g10/keyedit.c:750
msgid "You must select at least one key.\n"
msgstr "Vous devez sélectionner au moins une clé.\n"
-#: g10/keyedit.c:750
+#: g10/keyedit.c:752
msgid "keyedit.remove.subkey.okay"
msgstr ""
-#: g10/keyedit.c:752
+#: g10/keyedit.c:754
msgid "Do you really want to delete the selected keys? "
msgstr "Voulez-vous supprimer les clés sélectionnées? "
-#: g10/keyedit.c:753
+#: g10/keyedit.c:755
msgid "Do you really want to delete this key? "
msgstr "Voulez-vous vraiment supprimer cette clé? "
-#: g10/keyedit.c:800
+#: g10/keyedit.c:802
msgid "Invalid command (try \"help\")\n"
msgstr "Commande invalide (essayez \"help\")\n"
-#: g10/keyedit.c:1178
+#: g10/keyedit.c:1180
msgid "Please remove selections from the secret keys.\n"
-msgstr ""
+msgstr "Enlevez les sélections des clés secrètes.\n"
-#: g10/keyedit.c:1184
-#, fuzzy
+#: g10/keyedit.c:1186
msgid "Please select at most one secondary key.\n"
-msgstr "Vous devez sélectionner au moins une clé.\n"
+msgstr "Vous devez sélectionner au plus une clé secondaire.\n"
-#: g10/keyedit.c:1188
+#: g10/keyedit.c:1190
msgid "Changing exiration time for a secondary key.\n"
-msgstr ""
+msgstr "Changer la date d'expiration d'une clé secondaire.\n"
-#: g10/keyedit.c:1190
+#: g10/keyedit.c:1192
msgid "Changing exiration time for the primary key.\n"
-msgstr ""
+msgstr "Changer la date d'expiration de la clé primaire.\n"
-#: g10/keyedit.c:1234
+#: g10/keyedit.c:1236
msgid "No corresponding signature in secret ring\n"
-msgstr ""
+msgstr "Pas de signature correspondante dans le porte-clés secret\n"
-#: g10/keyedit.c:1294
+#: g10/keyedit.c:1296
#, c-format
msgid "No user id with index %d\n"
msgstr "Pas d'utilisateur avec l'index %d\n"
-#: g10/keyedit.c:1339
+#: g10/keyedit.c:1341
#, c-format
msgid "No secondary key with index %d\n"
msgstr "Pas de clé secondaire avec l'index %d\n"
-#: g10/mainproc.c:200
+#: g10/mainproc.c:201
#, c-format
msgid "public key decryption failed: %s\n"
msgstr "le décryptage de la clé publique a échoué: %s\n"
-#: g10/mainproc.c:230
+#: g10/mainproc.c:231
#, c-format
msgid "decryption failed: %s\n"
msgstr "le décryptage a échoué: %s\n"
-#: g10/mainproc.c:247
+#: g10/mainproc.c:248
msgid "note: sender requested \"for-your-eyes-only\"\n"
msgstr "note: l'expéditeur a demandé \"pour vos yeux seulement\"\n"
-#: g10/mainproc.c:848
+#: g10/mainproc.c:842
#, c-format
msgid "Signature made %.*s using %s key ID %08lX\n"
msgstr "Signature faite %.*s avec %s clé ID %08lX\n"
-#: g10/mainproc.c:856
+#: g10/mainproc.c:850
msgid "BAD signature from \""
msgstr "MAUVAISE signature de \""
-#: g10/mainproc.c:857
+#: g10/mainproc.c:851
msgid "Good signature from \""
msgstr "Bonne signature de \""
-#: g10/mainproc.c:868
+#: g10/mainproc.c:862
#, c-format
msgid "Can't check signature: %s\n"
msgstr "Ne peut vérifier la signature: %s\n"
#: g10/passphrase.c:141
msgid ""
"\n"
"You need a passphrase to unlock the secret key for\n"
"user: \""
msgstr ""
"\n"
"Vous avez besoin d'un mot de passe pour déverrouiller la clé secrète pour\n"
"l'utilisateur: \""
#: g10/passphrase.c:150
#, c-format
msgid "(%u-bit %s key, ID %08lX, created %s)\n"
msgstr "(clé de %u bits %s, ID %08lX, créée le %s)\n"
#: g10/passphrase.c:174
msgid "passphrase.enter"
msgstr ""
"Entrez le mot de passe; c'est une phrase secrète \n"
" Blurb, blurb,.... "
#: g10/passphrase.c:174
msgid "Enter pass phrase: "
msgstr "Entrez le mot de passe: "
#: g10/passphrase.c:177
msgid "passphrase.repeat"
msgstr ""
"Répétez le dernier mot de passe, pour être sûr de ce que vous avez tapé."
#: g10/passphrase.c:178
msgid "Repeat pass phrase: "
msgstr "Répétez le mot de passe: "
#: g10/plaintext.c:102
msgid "data not saved; use option \"--output\" to save it\n"
msgstr ""
"données non enregistrées; utilisez l'option \"--output\" pour\n"
"les enregistrer\n"
#: g10/plaintext.c:214
msgid "detached_signature.filename"
msgstr ""
#: g10/plaintext.c:215
msgid "Please enter name of data file: "
msgstr "Entrez le nom d'un fichier de données: "
#: g10/plaintext.c:299
#, c-format
msgid "can't open signed data '%s'\n"
msgstr "ne peut ouvir les données signées '%s'\n"
#: g10/seckey-cert.c:56
#, c-format
msgid "protection algorithm %d is not supported\n"
msgstr "l'algorithme de protection %d n'est pas supporté\n"
#: g10/seckey-cert.c:169
msgid "Invalid passphrase; please try again ...\n"
msgstr "Mot de passe invalide; réessayez...\n"
#: g10/seckey-cert.c:215
msgid "Warning: Weak key detected - please change passphrase again.\n"
msgstr "Attention: Mauvaise clé détectée - changez encore le mot de passe.\n"
#: g10/sig-check.c:155
msgid ""
"this is a PGP generated ElGamal key which is NOT secure for signatures!\n"
msgstr ""
"Cela est une clé ElGamal générée par PGP qui n'est PAS sûre pour les\n"
"signatures!\n"
#: g10/sig-check.c:165
msgid "public key created in future (time warp or clock problem)\n"
msgstr ""
"clé publique créée dans le futur (voyage dans le temps ou problème\n"
"d'horloge)\n"
#: g10/sig-check.c:170
#, c-format
msgid "warning: signature key expired %s\n"
msgstr "attention: la clé de signature a expiré le %s\n"
-#: g10/trustdb.c:122
+#: g10/trustdb.c:115
msgid "The trust DB is corrupted; please run \"gpgm --fix-trust-db\".\n"
msgstr ""
"La base de confiance est corrompue; exécutez \"gpgm --fix-trust-db\".\n"
-#: g10/trustdb.c:389
+#: g10/trustdb.c:379
#, c-format
msgid "chained sigrec %lu has a wrong owner\n"
msgstr "l'enregistrement de signature %lu a un mauvais propriétaire\n"
-#: g10/trustdb.c:436
-#, fuzzy, c-format
+#: g10/trustdb.c:426
+#, c-format
msgid "key %08lX: secret key without public key - skipped\n"
-msgstr "clé %08lX: clé secrète sans clé publique\n"
+msgstr "clé %08lX: clé secrète sans clé publique - non prise en compte\n"
-#: g10/trustdb.c:442
+#: g10/trustdb.c:432
#, c-format
msgid "key %08lX: secret and public key don't match\n"
msgstr "clé %08lX: les clés secrète et publique ne correspondent pas\n"
-#: g10/trustdb.c:452
+#: g10/trustdb.c:442
#, c-format
msgid "key %08lX: can't put it into the trustdb\n"
msgstr "clé %08lX: ne peut mettre cela dans la base de confiance\n"
-#: g10/trustdb.c:458
+#: g10/trustdb.c:448
#, c-format
msgid "key %08lX: query record failed\n"
msgstr "clé %08lX: l'enregistrement de requête a échoué\n"
-#: g10/trustdb.c:467
-#, fuzzy, c-format
+#: g10/trustdb.c:457
+#, c-format
msgid "key %08lX: already in secret key table\n"
-msgstr "clé %08lX: déjà dans le porte-clés secret\n"
+msgstr "clé %08lX: déjà dans la table des clés secrètes\n"
-#: g10/trustdb.c:470
-#, fuzzy, c-format
+#: g10/trustdb.c:460
+#, c-format
msgid "key %08lX: accepted as secret key.\n"
-msgstr "clé %08lX: déjà dans le porte-clés secret\n"
+msgstr "clé %08lX: acceptée comme clé secrète.\n"
-#: g10/trustdb.c:477
+#: g10/trustdb.c:467
#, c-format
msgid "enum_secret_keys failed: %s\n"
msgstr "enum_secret_keys a échoué: %s\n"
-#: g10/trustdb.c:1428
+#: g10/trustdb.c:1287 g10/trustdb.c:1351
+#, c-format
+msgid "%lu keys processed\n"
+msgstr "%lu clés traitées\n"
+
+#: g10/trustdb.c:1289
+#, c-format
+msgid "\t%lu keys skipped\n"
+msgstr "\t%lu clés non prises en compte\n"
+
+#: g10/trustdb.c:1291 g10/trustdb.c:1353
+#, c-format
+msgid "\t%lu keys with errors\n"
+msgstr "\t%lu clés avec erreurs\n"
+
+#: g10/trustdb.c:1293 g10/trustdb.c:1355
+#, c-format
+msgid "\t%lu keys updated\n"
+msgstr "\t%lu clés mises à jour\n"
+
+#: g10/trustdb.c:1357
+#, c-format
+msgid "\t%lu keys inserted\n"
+msgstr "\t%lu clés insérées\n"
+
+#: g10/trustdb.c:1412
#, c-format
msgid "key %08lX: insert trust record failed: %s\n"
msgstr "clé %08lX: l'insertion d'enregistrement de confiance a échoué: %s\n"
-#: g10/trustdb.c:1432
+#: g10/trustdb.c:1416
#, c-format
msgid "key %08lX.%lu: inserted into trustdb\n"
msgstr "clé %08lX.%lu: insérée dans la base de confiance\n"
-#: g10/trustdb.c:1440
+#: g10/trustdb.c:1424
#, c-format
msgid "key %08lX.%lu: created in future (time warp or clock problem)\n"
msgstr "clé %08lX.%lu: créée dans le futur (problème d'horloge)\n"
-#: g10/trustdb.c:1447
+#: g10/trustdb.c:1431
#, c-format
msgid "key %08lX.%lu: expired at %s\n"
msgstr "clé %08lX.%lu: a expiré le %s\n"
-#: g10/trustdb.c:1455
+#: g10/trustdb.c:1439
#, c-format
msgid "key %08lX.%lu: trust check failed: %s\n"
msgstr "clé %08lX.%lu: la vérification de confiance a échoué: %s\n"
-#: g10/trustdb.c:1718
+#: g10/trustdb.c:1656
#, c-format
msgid "note: sig rec %lu[%d] in hintlist of %lu but marked as checked\n"
msgstr ""
+"note: enregistrement de signature %lu[%d] dans la liste d'aide de %lu mais\n"
+"mais marqué comme vérifié\n"
-#: g10/trustdb.c:1722
+#: g10/trustdb.c:1660
#, c-format
msgid "note: sig rec %lu[%d] in hintlist of %lu but not marked\n"
msgstr ""
+"note: enregistrement de signature %lu[%d] dans la liste d'aide de %lu mais\n"
+"non marqué\n"
#. we need the dir record
-#: g10/trustdb.c:1729
+#: g10/trustdb.c:1667
#, c-format
msgid "sig rec %lu[%d] in hintlist of %u does not point to a dir record\n"
msgstr ""
+"l'enregistrement de signature %lu[%d] dans la liste d'aide de %u ne pointe\n"
+"pas vers un enregistrement de répertoire\n"
-#: g10/trustdb.c:1735
+#: g10/trustdb.c:1673
#, c-format
msgid "lid %lu: no primary key\n"
-msgstr ""
+msgstr "lid %lu: pas de clé primaire\n"
-#: g10/trustdb.c:1764
-#, fuzzy, c-format
+#: g10/trustdb.c:1705
+#, c-format
msgid "lid %lu: user id not found in keyblock\n"
-msgstr "%s: utilisateur non trouvé\n"
+msgstr "lid %lu: utilisateur non trouvé dans le bloc de clés\n"
-#: g10/trustdb.c:1771
+#: g10/trustdb.c:1709
+#, c-format
+msgid "lid %lu: user id without signature\n"
+msgstr "lid %lu: utilisateur sans signature\n"
+
+#: g10/trustdb.c:1716
#, c-format
msgid "lid %lu: self-signature in hintlist\n"
-msgstr ""
+msgstr "lid %lu: auto-signature dans la liste d'aide\n"
-#: g10/trustdb.c:1776
-#, fuzzy, c-format
-msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: good signature (3)\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+#: g10/trustdb.c:1721
+#, c-format
+msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: Good signature (3)\n"
+msgstr "clé %08lX.%lu, uid %02X%02X, sig %08lX: Bonne signature (3)\n"
-#: g10/trustdb.c:1783
-#, fuzzy, c-format
+#: g10/trustdb.c:1728
+#, c-format
msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: very strange: no public key\n"
msgstr ""
-"clé %08lX.%lu, uid %02X%02X: pas de clé publique pour la signature %08lX\n"
+"clé %08lX.%lu, uid %02X%02X, sig %08lX: très étrange: pas de clé publique\n"
-#: g10/trustdb.c:1790 g10/trustdb.c:2225 g10/trustdb.c:2297
-#, fuzzy, c-format
-msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: invalid signature: %s\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+#: g10/trustdb.c:1735 g10/trustdb.c:2207 g10/trustdb.c:2280
+#, c-format
+msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: %s\n"
+msgstr "clé %08lX.%lu, uid %02X%02X, sig %08lX: %s\n"
-#: g10/trustdb.c:1835
+#: g10/trustdb.c:1779
#, c-format
msgid "hintlist %lu[%d] of %lu does not point to a dir record\n"
msgstr ""
+"la liste d'aide de %lu[%d] de %lu ne pointe pas vers un enregistrement\n"
+"de répertoire\n"
-#: g10/trustdb.c:1841
+#: g10/trustdb.c:1785
#, c-format
msgid "lid %lu does not have a key\n"
-msgstr ""
+msgstr "la lid %lu n'a pas de clé\n"
-#: g10/trustdb.c:1851
-#, fuzzy, c-format
+#: g10/trustdb.c:1795
+#, c-format
msgid "lid %lu: can't get keyblock: %s\n"
-msgstr "ne peut écrire le bloc de clés: %s\n"
+msgstr "lid %lu: ne peut obtenir le bloc de clés: %s\n"
-#: g10/trustdb.c:2096
-#, fuzzy, c-format
-msgid "key %08lX.%lu, uid %02X%02X: good self-signature\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+#: g10/trustdb.c:2085
+#, c-format
+msgid "key %08lX.%lu, uid %02X%02X: Good self-signature\n"
+msgstr "clé %08lX.%lu, uid %02X%02X: Bonne auto-signature\n"
+
+#: g10/trustdb.c:2091
+#, c-format
+msgid "key %08lX, uid %02X%02X: Invalid self-signature: %s\n"
+msgstr "clé %08lX.%lu, uid %02X%02X: Auto-signature invalide: %s\n"
-#: g10/trustdb.c:2188
-#, fuzzy, c-format
+#: g10/trustdb.c:2171
+#, c-format
msgid ""
"key %08lX.%lu, uid %02X%02X, sig %08lX: duplicated signature - deleted\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+msgstr ""
+"clé %08lX.%lu, uid %02X%02X, sig %08lX: signature dupliquée - effacée\n"
-#: g10/trustdb.c:2211
-#, fuzzy, c-format
-msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: good signature (1)\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+#: g10/trustdb.c:2193
+#, c-format
+msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: Good signature (1)\n"
+msgstr "clé %08lX.%lu, uid %02X%02X, sig %08lX: Bonne signature (1)\n"
-#: g10/trustdb.c:2218
-#, fuzzy, c-format
+#: g10/trustdb.c:2200
+#, c-format
msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: weird: no public key\n"
-msgstr ""
-"clé %08lX.%lu, uid %02X%02X: pas de clé publique pour la signature %08lX\n"
+msgstr "clé %08lX.%lu, uid %02X%02X, sig %08lX: bizarre: pas de clé publique\n"
-#: g10/trustdb.c:2240
-#, fuzzy, c-format
+#: g10/trustdb.c:2222
+#, c-format
msgid "key %08lX.%lu, uid %02X%02X: has shadow dir %lu but not yet marked.\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+msgstr ""
+"clé %08lX.%lu, uid %02X%02X: possède une ombre %lu mais pas encore\n"
+"marquée.\n"
-#: g10/trustdb.c:2279
-#, fuzzy, c-format
-msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: good signature (2)\n"
-msgstr "clé %08lXi.%lu, uid %02X%02X: %ssignature invalide: %s\n"
+#: g10/trustdb.c:2263
+#, c-format
+msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: Good signature (2)\n"
+msgstr "clé %08lX.%lu, uid %02X%02X, sig %08lX: Bonne signature (2)\n"
-#: g10/trustdb.c:2290
-#, fuzzy, c-format
+#: g10/trustdb.c:2272
+#, c-format
msgid "key %08lX.%lu, uid %02X%02X, sig %08lX: no public key\n"
-msgstr ""
-"clé %08lX.%lu, uid %02X%02X: pas de clé publique pour la signature %08lX\n"
+msgstr "clé %08lX.%lu, uid %02X%02X, sig %08lX: pas de clé publique\n"
#. key binding
-#: g10/trustdb.c:2386
-#, fuzzy, c-format
+#: g10/trustdb.c:2364
+#, c-format
msgid "key %08lX: bogus key binding by %08lX\n"
-msgstr "clé %08lX: pas de nom d'utilisateur\n"
+msgstr "clé %08lX: mauvaise association avec %08lX\n"
#. key revocation
-#: g10/trustdb.c:2390
-#, fuzzy, c-format
+#: g10/trustdb.c:2368
+#, c-format
msgid "key %08lX: bogus key revocation by %08lX\n"
-msgstr "clé %08lX: clé publique pas trouvée: %s\n"
+msgstr "clé %08lX: mauvaise révocation de clé par %08lX\n"
#. subkey revocation
-#: g10/trustdb.c:2394
-#, fuzzy, c-format
+#: g10/trustdb.c:2372
+#, c-format
msgid "key %08lX: bogus subkey revocation by %08lX\n"
-msgstr "clé %08lX: clé publique pas trouvée: %s\n"
+msgstr "clé %08lX: mauvaise révocation de sous-clé par %08lX\n"
#: g10/status.c:246
msgid "No help available"
msgstr "Pas d'aide disponible"
#: g10/status.c:252
#, c-format
msgid "No help available for '%s'"
msgstr "Pas d'aide disponible pour '%s'"
#: g10/pubkey-enc.c:78
#, c-format
msgid "anonymous receiver; trying secret key %08lX ...\n"
msgstr "destinataire anonyme; essai de la clé secrète %08lX ...\n"
#: g10/pubkey-enc.c:84
msgid "okay, we are the anonymous receiver.\n"
msgstr "d'accord, nous sommes le destinataire anonyme.\n"
#: g10/pubkey-enc.c:183
#, c-format
msgid "note: cipher algorithm %d not found in preferences\n"
msgstr ""
"note: l'algorithme de cryptage %d n'a pas été trouvé dans les préférences\n"
#. do not overwrite
#: g10/openfile.c:58
#, c-format
msgid "File '%s' exists. "
msgstr "Le fichier '%s' existe. "
#: g10/openfile.c:59
msgid "openfile.overwrite.okay"
msgstr ""
#: g10/openfile.c:60
msgid "Overwrite (y/N)? "
msgstr "Réécrire (o/N)? "
+#: g10/openfile.c:85
+msgid "writing to stdout\n"
+msgstr "écriture vers la sortie standard\n"
+
+#: g10/openfile.c:101 g10/openfile.c:165
+#, c-format
+msgid "%s: can't create: %s\n"
+msgstr "%s: ne peut créer: %s\n"
+
+#: g10/openfile.c:134
+#, c-format
+msgid "assuming signed data in '%s'\n"
+msgstr "les données signées sont supposées être dans '%s'\n"
+
+#: g10/openfile.c:181
+#, c-format
+msgid "%s: new options file created\n"
+msgstr "%s: nouveau fichier d'options créé\n"
+
#: g10/encr-data.c:74
msgid ""
"Warning: Message was encrypted with a weak key in the symmetric cipher.\n"
msgstr ""
"Attention: Le message a été crypté avec une mauvaise clé pendant le\n"
"chiffrement symétrique.\n"
#: g10/seskey.c:52
msgid "weak key created - retrying\n"
-msgstr "mauvais clé générée - nouvel essai\n"
+msgstr "mauvaise clé générée - nouvel essai\n"
#: g10/seskey.c:57
#, c-format
msgid "cannot avoid weak key for symmetric cipher; tried %d times!\n"
msgstr ""
"ne peut éviter une mauvaise clé pour le chiffrement symétrique: \n"
"%d essais ont eu lieu!\n"
-
-#~ msgid "key %08lX: already in ultikey_table\n"
-#~ msgstr "clé %08lX: déja dans ultikey_table\n"
diff --git a/util/ChangeLog b/util/ChangeLog
index c58f9f7e8..af18ddce5 100644
--- a/util/ChangeLog
+++ b/util/ChangeLog
@@ -1,183 +1,197 @@
+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/argparse.c b/util/argparse.c
index 766d461f9..a75ef3467 100644
--- a/util/argparse.c
+++ b/util/argparse.c
@@ -1,825 +1,895 @@
/* [argparse.c wk 17.06.97] Argument Parser for option handling
* Copyright (C) 1998 Free Software Foundation, Inc.
- * This file is part of WkLib.
+ * This file is part of GnuPG.
*
- * WkLib is free software; you can redistribute it and/or modify
+ * GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
- * WkLib is distributed in the hope that it will be useful,
+ * GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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
*
*
* Note: This is an independent version of the one in WkLib
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "util.h"
#include "i18n.h"
/*********************************
* @Summary arg_parse
* #include <wk/lib.h>
*
* typedef struct {
* char *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 index;
* const char *last;
* } internal; DO NOT CHANGE
* } ARGPARSE_ARGS;
*
* typedef struct {
* int short_opt;
* const char *long_opt;
* unsigned flags;
* } ARGPARSE_OPTS;
*
* int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
*
* @Description
* This is my replacement for getopt(). See the example for a typical usage.
* Global flags are:
* Bit 0 : Do not remove options form argv
* Bit 1 : Do not stop at last option but return other args
* with r_opt set to -1.
* Bit 2 : Assume options and real args are mixed.
* Bit 3 : Do not use -- to stop option processing.
* Bit 4 : Do not skip the first arg.
* Bit 5 : allow usage of long option with only one dash
* Bit 6 : ignore --version
- * all other bits must be set to zero, this value is modified by the function
- * so assume this is write only.
+ * all other bits must be set to zero, this value is modified by the
+ * function, so assume this is write only.
* Local flags (for each option):
* Bit 2-0 : 0 = does not take an argument
* 1 = takes int argument
* 2 = takes string argument
* 3 = takes long argument
* 4 = takes ulong argument
* Bit 3 : argument is optional (r_type will the be set to 0)
* Bit 4 : allow 0x etc. prefixed values.
- * Bit 7 : this is an command and not an option
- * If can stop the option processing by setting opts to NULL, the function will
+ * Bit 7 : this is a command and not an option
+ * You stop the option processing by setting opts to NULL, the function will
* then return 0.
* @Return Value
* Returns the args.r_opt or 0 if ready
* r_opt may be -2/-7 to indicate an unknown option/command.
* @See Also
* ArgExpand
* @Notes
* You do not need to process the options 'h', '--help' or '--version'
* because this function includes standard help processing; but if you
* specify '-h', '--help' or '--version' you have to do it yourself.
* The option '--' stops argument processing; if bit 1 is set the function
* continues to return normal arguments.
* To process float args or unsigned args you must use a string args and do
* the conversion yourself.
* @Example
*
* ARGPARSE_OPTS opts[] = {
* { 'v', "verbose", 0 },
* { 'd', "debug", 0 },
* { 'o', "output", 2 },
* { 'c', "cross-ref", 2|8 },
* { 'm', "my-option", 1|8 },
* { 500, "have-no-short-option-for-this-long-option", 0 },
* {0} };
* ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
*
* while( ArgParse( &pargs, &opts) ) {
* switch( pargs.r_opt ) {
* case 'v': opt.verbose++; break;
* case 'd': opt.debug++; break;
* case 'o': opt.outfile = pargs.r.ret_str; break;
* case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
* case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
* case 500: opt.a_long_one++; break
* default : pargs.err = 1; break; -- force warning output --
* }
* }
* if( argc > 1 )
* log_fatal( "Too many args");
*
*/
static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
static void show_version(void);
static void
initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
{
if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
arg->internal.index = 0;
arg->internal.last = NULL;
arg->internal.inarg = 0;
arg->internal.stopped= 0;
arg->err = 0;
arg->flags |= 1<<15; /* mark initialized */
if( *arg->argc < 0 )
log_bug("Invalid argument for ArgParse\n");
}
if( arg->err ) { /* last option was erroneous */
const char *s;
if( filename ) {
if( arg->r_opt == -6 )
s = "%s:%u: argument not expected\n";
else if( arg->r_opt == -5 )
s = "%s:%u: read error\n";
else if( arg->r_opt == -4 )
s = "%s:%u: keyword too long\n";
else if( arg->r_opt == -3 )
s = "%s:%u: missing argument\n";
else if( arg->r_opt == -7 )
s = "%s:%u: invalid command\n";
else
s = "%s:%u: invalid option\n";
log_error(s, filename, *lineno );
}
else {
if( arg->r_opt == -3 )
s = "Missing argument for option \"%.50s\"\n";
+ else if( arg->r_opt == -6 )
+ s = "Option \"%.50s\" does not expect an argument\n";
else if( arg->r_opt == -7 )
s = "Invalid command \"%.50s\"\n";
+ else if( arg->r_opt == -8 )
+ s = "Option \"%.50s\" is ambiguous\n";
+ else if( arg->r_opt == -9 )
+ s = "Command \"%.50s\" is ambiguous\n";
else
s = "Invalid option \"%.50s\"\n";
log_error(s, arg->internal.last? arg->internal.last:"[??]" );
}
if( arg->err != 1 )
exit(2);
arg->err = 0;
}
}
/****************
* Get options from a file.
* Lines starting with '#' are comment lines.
* Syntax is simply a keyword and the argument.
* Valid keywords are all keywords from the long_opt list without
- * the leading dashes. The special keywords help, warranty and version
+ * the leading dashes. The special keywords "help", "warranty" and "version"
* are not valid here.
* Caller must free returned strings.
* If called with FP set to NULL command line args are parse instead.
+ *
+ * Q: Should we allow the syntax
+ * keyword = value
+ * and accept for boolean options a value of 1/0, yes/no or true/false?
+ * Note: Abbreviation of options is here not allowed.
*/
int
optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
{
int state, i, c;
int index=0;
char keyword[100];
char *buffer = NULL;
size_t buflen = 0;
int inverse=0;
if( !fp ) /* same as arg_parse() in this case */
return arg_parse( arg, opts );
initialize( arg, filename, lineno );
/* find the next keyword */
state = i = 0;
for(;;) {
c=getc(fp);
if( c == '\n' || c== EOF ) {
if( c != EOF )
++*lineno;
if( state == -1 )
break;
else if( state == 2 ) {
keyword[i] = 0;
for(i=0; opts[i].short_opt; i++ )
if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
break;
index = i;
arg->r_opt = opts[index].short_opt;
if( inverse )
arg->r_opt = -arg->r_opt;
if( !opts[index].short_opt ) /* unknown command/option */
arg->r_opt = (opts[index].flags & 256)? -7:-2;
else if( (opts[index].flags & 8) ) /* no argument */
arg->r_opt = -3; /* error */
- else /* no or optiona argument */
+ else /* no or optional argument */
arg->r_type = 0; /* okay */
break;
}
else if( state == 3 ) { /* no argument found */
if( !(opts[index].flags & 7) ) /* does not take an argument */
arg->r_type = 0; /* okay */
else if( (opts[index].flags & 8) ) /* no optional argument */
arg->r_type = 0; /* okay */
else /* no required argument */
arg->r_opt = -3; /* error */
break;
}
else if( state == 4 ) { /* have an argument */
if( !(opts[index].flags & 7) ) /* does not take an argument */
arg->r_opt = -6; /* error */
else {
if( !buffer ) {
keyword[i] = 0;
buffer = m_strdup(keyword);
}
else
buffer[i] = 0;
+ trim_spaces( buffer );
if( !set_opt_arg(arg, opts[index].flags, buffer) )
m_free(buffer);
}
break;
}
else if( c == EOF ) {
if( ferror(fp) )
arg->r_opt = -5; /* read error */
else
arg->r_opt = 0; /* eof */
break;
}
state = 0;
i = 0;
}
else if( state == -1 )
; /* skip */
else if( !state && isspace(c) )
; /* skip leading white space */
else if( !state && c == '#' )
state = 1; /* start of a comment */
else if( state == 1 )
; /* skip comments */
else if( state == 2 && isspace(c) ) {
keyword[i] = 0;
for(i=0; opts[i].short_opt; i++ )
if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
break;
index = i;
arg->r_opt = opts[index].short_opt;
if( !opts[index].short_opt ) {
arg->r_opt = (opts[index].flags & 256)? -7:-2;
state = -1; /* skip rest of line and leave */
}
else
state = 3;
}
else if( state == 3 ) { /* skip leading spaces of the argument */
if( !isspace(c) ) {
i = 0;
keyword[i++] = c;
state = 4;
}
}
else if( state == 4 ) { /* collect the argument */
if( buffer ) {
if( i < buflen-1 )
buffer[i++] = c;
else {
buflen += 50;
buffer = m_realloc(buffer, buflen);
buffer[i++] = c;
}
}
else if( i < DIM(keyword)-1 )
keyword[i++] = c;
else {
buflen = DIM(keyword)+50;
buffer = m_alloc(buflen);
memcpy(buffer, keyword, i);
buffer[i++] = c;
}
}
else if( i >= DIM(keyword)-1 ) {
arg->r_opt = -4; /* keyword to long */
state = -1; /* skip rest of line and leave */
}
else {
keyword[i++] = c;
state = 2;
}
}
return arg->r_opt;
}
+static int
+find_long_option( ARGPARSE_OPTS *opts, const char *keyword )
+{
+ int i;
+ size_t n;
+
+ /* Would be better if we can do a binary search, but it is not
+ possible to reorder our option table because we would mess
+ up our help strings - What we can do is: Build a nice option
+ lookup table wehn this function is first invoked */
+ if( !*keyword )
+ return -1;
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
+ return i;
+ /* not found, see whether it is an abbreviation */
+ n = strlen( keyword );
+ for(i=0; opts[i].short_opt; i++ ) {
+ if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
+ int j;
+ for(j=i+1; opts[j].short_opt; j++ ) {
+ if( opts[j].long_opt
+ && !strncmp( opts[j].long_opt, keyword, n ) )
+ return -2; /* abbreviation is ambiguous */
+ }
+ return i;
+ }
+ }
+ return -1;
+}
+
int
arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
{
int index;
int argc;
char **argv;
char *s, *s2;
int i;
initialize( arg, NULL, NULL );
argc = *arg->argc;
argv = *arg->argv;
index = arg->internal.index;
if( !index && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
argc--; argv++; index++;
}
next_one:
if( !argc ) { /* no more args */
arg->r_opt = 0;
goto leave; /* ready */
}
s = *argv;
arg->internal.last = s;
if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
arg->r_opt = -1; /* not an option but a argument */
arg->r_type = 2;
arg->r.ret_str = s;
argc--; argv++; index++; /* set to next one */
}
else if( arg->internal.stopped ) { /* ready */
arg->r_opt = 0;
goto leave;
}
else if( *s == '-' && s[1] == '-' ) { /* long option */
+ char *argpos;
+
arg->internal.inarg = 0;
if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
arg->internal.stopped = 1;
argc--; argv++; index++;
goto next_one;
}
- for(i=0; opts[i].short_opt; i++ )
- if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+2) )
- break;
+ argpos = strchr( s+2, '=' );
+ if( argpos )
+ *argpos = 0;
+ i = find_long_option( opts, s+2 );
+ if( argpos )
+ *argpos = '=';
- if( !opts[i].short_opt && !strcmp( "help", s+2) )
+ if( i < 0 && !strcmp( "help", s+2) )
show_help(opts, arg->flags);
- else if( !opts[i].short_opt && !strcmp( "version", s+2) ) {
+ else if( i < 0 && !strcmp( "version", s+2) ) {
if( !(arg->flags & (1<<6)) ) {
show_version();
exit(0);
}
}
- else if( !opts[i].short_opt && !strcmp( "warranty", s+2) ) {
+ else if( i < 0 && !strcmp( "warranty", s+2) ) {
puts( strusage(16) );
exit(0);
}
+ else if( i < 0 && !strcmp( "dump-options", s+2) ) {
+ for(i=0; opts[i].short_opt; i++ )
+ if( opts[i].long_opt )
+ printf( "--%s\n", opts[i].long_opt );
+ exit(0);
+ }
- arg->r_opt = opts[i].short_opt;
- if( !opts[i].short_opt ) {
+ if( i == -2 ) /* ambiguous option */
+ arg->r_opt = (opts[i].flags & 256)? -9:-8;
+ else if( i == -1 ) {
arg->r_opt = (opts[i].flags & 256)? -7:-2;
arg->r.ret_str = s+2;
}
+ else
+ arg->r_opt = opts[i].short_opt;
+ if( i < 0 )
+ ;
else if( (opts[i].flags & 7) ) {
- s2 = argv[1];
+ if( argpos ) {
+ s2 = argpos+1;
+ if( !*s2 )
+ s2 = NULL;
+ }
+ else
+ s2 = argv[1];
if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
arg->r_type = 0; /* because it is optional */
}
else if( !s2 ) {
arg->r_opt = -3; /* missing argument */
}
- else if( *s2 == '-' && (opts[i].flags & 8) ) {
+ else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
/* the argument is optional and the next seems to be
* an option. We do not check this possible option
* but assume no argument */
arg->r_type = 0;
}
else {
set_opt_arg(arg, opts[i].flags, s2);
- argc--; argv++; index++; /* skip one */
+ if( !argpos ) {
+ argc--; argv++; index++; /* skip one */
+ }
}
}
else { /* does not take an argument */
- arg->r_type = 0;
+ if( argpos )
+ arg->r_type = -6; /* argument not expected */
+ else
+ arg->r_type = 0;
}
argc--; argv++; index++; /* set to next one */
}
else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
int dash_kludge = 0;
i = 0;
if( !arg->internal.inarg ) {
arg->internal.inarg++;
if( arg->flags & (1<<5) ) {
for(i=0; opts[i].short_opt; i++ )
if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
dash_kludge=1;
break;
}
}
}
s += arg->internal.inarg;
if( !dash_kludge ) {
for(i=0; opts[i].short_opt; i++ )
if( opts[i].short_opt == *s )
break;
}
if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
show_help(opts, arg->flags);
arg->r_opt = opts[i].short_opt;
if( !opts[i].short_opt ) {
arg->r_opt = (opts[i].flags & 256)? -7:-2;
arg->internal.inarg++; /* point to the next arg */
arg->r.ret_str = s;
}
else if( (opts[i].flags & 7) ) {
if( s[1] && !dash_kludge ) {
s2 = s+1;
set_opt_arg(arg, opts[i].flags, s2);
}
else {
s2 = argv[1];
if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
arg->r_type = 0; /* because it is optional */
}
else if( !s2 ) {
arg->r_opt = -3; /* missing argument */
}
else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
/* the argument is optional and the next seems to be
* an option. We do not check this possible option
* but assume no argument */
arg->r_type = 0;
}
else {
set_opt_arg(arg, opts[i].flags, s2);
argc--; argv++; index++; /* skip one */
}
}
s = "x"; /* so that !s[1] yields false */
}
else { /* does not take an argument */
arg->r_type = 0;
arg->internal.inarg++; /* point to the next arg */
}
if( !s[1] || dash_kludge ) { /* no more concatenated short options */
arg->internal.inarg = 0;
argc--; argv++; index++;
}
}
else if( arg->flags & (1<<2) ) {
arg->r_opt = -1; /* not an option but a argument */
arg->r_type = 2;
arg->r.ret_str = s;
argc--; argv++; index++; /* set to next one */
}
else {
arg->internal.stopped = 1; /* stop option processing */
goto next_one;
}
leave:
*arg->argc = argc;
*arg->argv = argv;
arg->internal.index = index;
return arg->r_opt;
}
static int
set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
{
int base = (flags & 16)? 0 : 10;
switch( arg->r_type = (flags & 7) ) {
case 1: /* takes int argument */
arg->r.ret_int = (int)strtol(s,NULL,base);
return 0;
case 3: /* takes long argument */
arg->r.ret_long= strtol(s,NULL,base);
return 0;
case 4: /* takes ulong argument */
arg->r.ret_ulong= strtoul(s,NULL,base);
return 0;
case 2: /* takes string argument */
default:
arg->r.ret_str = s;
return 1;
}
}
static size_t
long_opt_strlen( ARGPARSE_OPTS *o )
{
size_t n = strlen(o->long_opt);
if( o->description && *o->description == '|' ) {
const char *s;
s=o->description+1;
if( *s != '=' )
n++;
for(; *s && *s != '|'; s++ )
n++;
}
return n;
}
/****************
* Print formatted help. The description string has some special
* meanings:
* - A description string which is "@" suppresses help output for
* this option
* - a description,ine which starts with a '@' and is followed by
* any other characters is printed as is; this may be used for examples
* ans such.
* - A description which starts with a '|' outputs the string between this
* bar and the next one as arguments of the long option.
*/
static void
show_help( ARGPARSE_OPTS *opts, unsigned flags )
{
const char *s;
show_version();
putchar('\n');
s = strusage(41);
puts(s);
if( opts[0].description ) { /* auto format the option description */
int i,j, indent;
/* get max. length of long options */
for(i=indent=0; opts[i].short_opt; i++ ) {
if( opts[i].long_opt )
if( !opts[i].description || *opts[i].description != '@' )
if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
indent = j;
}
/* example: " -v, --verbose Viele Sachen ausgeben" */
indent += 10;
if( *opts[0].description != '@' )
puts("Options:");
for(i=0; opts[i].short_opt; i++ ) {
s = _( opts[i].description );
if( s && *s== '@' && !s[1] ) /* hide this line */
continue;
if( s && *s == '@' ) { /* unindented comment only line */
for(s++; *s; s++ ) {
if( *s == '\n' ) {
if( s[1] )
putchar('\n');
}
else
putchar(*s);
}
putchar('\n');
continue;
}
j = 3;
if( opts[i].short_opt < 256 ) {
printf(" -%c", opts[i].short_opt );
if( !opts[i].long_opt ) {
if(s && *s == '|' ) {
putchar(' '); j++;
for(s++ ; *s && *s != '|'; s++, j++ )
putchar(*s);
if( *s )
s++;
}
}
}
else
fputs(" ", stdout);
if( opts[i].long_opt ) {
j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
opts[i].long_opt );
if(s && *s == '|' ) {
if( *++s != '=' ) {
putchar(' ');
j++;
}
for( ; *s && *s != '|'; s++, j++ )
putchar(*s);
if( *s )
s++;
}
fputs(" ", stdout);
j += 3;
}
for(;j < indent; j++ )
putchar(' ');
if( s ) {
if( *s && j > indent ) {
putchar('\n');
for(j=0;j < indent; j++ )
putchar(' ');
}
for(; *s; s++ ) {
if( *s == '\n' ) {
if( s[1] ) {
putchar('\n');
for(j=0;j < indent; j++ )
putchar(' ');
}
}
else
putchar(*s);
}
}
putchar('\n');
}
if( flags & 32 )
puts("\n(A single dash may be used instead of the double ones)");
}
if( (s=strusage(19)) ) { /* bug reports to ... */
putchar('\n');
fputs(s, stdout);
}
fflush(stdout);
exit(0);
}
static void
show_version()
{
const char *s;
int i;
/* version line */
fputs(strusage(11), stdout);
if( (s=strusage(12)) )
printf(" (%s)", s );
printf(" %s\n", strusage(13) );
/* additional version lines */
for(i=20; i < 30; i++ )
if( (s=strusage(i)) )
printf("%s\n", s );
/* copyright string */
if( (s=strusage(14)) )
printf("%s\n", s );
/* copying conditions */
if( (s=strusage(15)) )
fputs(s, stdout);
/* thanks */
if( (s=strusage(18)) )
fputs(s, stdout);
/* additional program info */
for(i=30; i < 40; i++ )
if( (s=strusage(i)) )
fputs(s, stdout);
fflush(stdout);
}
void
usage( int level )
{
if( !level ) {
fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
strusage(14) );
fflush(stderr);
}
else if( level == 1 ) {
fputs(strusage(40),stderr);
exit(2);
}
else if( level == 2 ) {
puts(strusage(41));
exit(0);
}
}
/* Level
* 0: Copyright String auf stderr ausgeben
* 1: Kurzusage auf stderr ausgeben und beenden
* 2: Langusage auf stdout ausgeben und beenden
* 11: name of program
* 12: optional name of package which includes this program.
* 13: version string
* 14: copyright string
* 15: Short copying conditions (with LFs)
* 16: Long copying conditions (with LFs)
* 17: Optional printable OS name
* 18: Optional thanks list (with LFs)
* 19: Bug report info
*20..29: Additional lib version strings.
*30..39: Additional program info (with LFs)
* 40: short usage note (with LF)
* 41: long usage note (with LF)
*/
const char *
default_strusage( int level )
{
const char *p = NULL;
switch( level ) {
case 11: p = "foo"; break;
case 13: p = "0.0"; break;
case 14: p = "Copyright (C) 1998 Free Software Foundation, Inc."; break;
case 15: p =
"This program comes with ABSOLUTELY NO WARRANTY.\n"
"This is free software, and you are welcome to redistribute it\n"
"under certain conditions. See the file COPYING for details.\n"; break;
case 16: p =
"This is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n\n"
"It is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
break;
case 40: /* short and long usage */
case 41: p = ""; break;
}
return p;
}
#ifdef TEST
static struct {
int verbose;
int debug;
char *outfile;
char *crf;
int myopt;
int echo;
int a_long_one;
}opt;
int
main(int argc, char **argv)
{
ARGPARSE_OPTS opts[] = {
{ 'v', "verbose", 0 , "Laut sein"},
{ 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
{ 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"},
{ 'o', "output", 2 },
{ 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
{ 'm', "my-option", 1|8 },
{ 500, "a-long-option", 0 },
{0} };
ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
int i;
while( ArgParse( &pargs, opts) ) {
switch( pargs.r_opt ) {
case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break;
case 'v': opt.verbose++; break;
case 'e': opt.echo++; break;
case 'd': opt.debug++; break;
case 'o': opt.outfile = pargs.r.ret_str; break;
case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
case 500: opt.a_long_one++; break;
default : pargs.err = 1; break; /* force warning output */
}
}
for(i=0; i < argc; i++ )
printf("%3d -> (%s)\n", i, argv[i] );
puts("Options:");
if( opt.verbose )
printf(" verbose=%d\n", opt.verbose );
if( opt.debug )
printf(" debug=%d\n", opt.debug );
if( opt.outfile )
printf(" outfile='%s'\n", opt.outfile );
if( opt.crf )
printf(" crffile='%s'\n", opt.crf );
if( opt.myopt )
printf(" myopt=%d\n", opt.myopt );
if( opt.a_long_one )
printf(" a-long-one=%d\n", opt.a_long_one );
if( opt.echo )
printf(" echo=%d\n", opt.echo );
return 0;
}
#endif
/**** bottom of file ****/
diff --git a/util/strgutil.c b/util/strgutil.c
index 809b0c3f2..217b64043 100644
--- a/util/strgutil.c
+++ b/util/strgutil.c
@@ -1,223 +1,269 @@
/* 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 <config.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "types.h"
#include "util.h"
#include "memory.h"
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));
strcpy(sl->d, string);
sl->next = *list;
*list = sl;
return sl;
}
STRLIST
append_to_strlist( STRLIST *list, const char *string )
{
STRLIST r, sl;
sl = m_alloc( sizeof *sl + strlen(string));
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
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 ;
}
int
string_count_chr( const char *string, int c )
{
int count;
for(count=0; *string; string++ )
if( *string == c )
count++;
return count;
}
+
+/****************
+ * Convert string, which is in native encoding to UTF8 and return the
+ * new allocated UTF8 string.
+ * This code assumes that native is iso-8859-1.
+ */
+char *
+native_to_utf8( const char *string )
+{
+ const byte *s;
+ char *buffer;
+ byte *p;
+ size_t length=0;
+
+ 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".
+ * This code assumes that native is iso-8859-1.
+ */
+char *
+utf8_to_native( const char *string )
+{
+ /* FIXME: Not yet done */
+ return m_strdup(string);
+}
+
+
/*********************************************
********** 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

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 8, 7:20 AM (1 d, 8 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
76/55/08b282c338df734e8c9a12c2b334

Event Timeline