diff options
Diffstat (limited to 'gpglib')
| -rw-r--r-- | gpglib/.gitignore | 6 | ||||
| -rw-r--r-- | gpglib/Makefile.am | 45 | ||||
| -rw-r--r-- | gpglib/README.html | 357 | ||||
| -rw-r--r-- | gpglib/checksign.c | 75 | ||||
| -rw-r--r-- | gpglib/configure.in | 120 | ||||
| -rw-r--r-- | gpglib/delete.c | 77 | ||||
| -rw-r--r-- | gpglib/export.c | 64 | ||||
| -rw-r--r-- | gpglib/fork.c | 359 | ||||
| -rw-r--r-- | gpglib/genkey.c | 209 | ||||
| -rw-r--r-- | gpglib/gpg.c | 2235 | ||||
| -rw-r--r-- | gpglib/gpglib.h | 239 | ||||
| -rw-r--r-- | gpglib/import.c | 70 | ||||
| -rw-r--r-- | gpglib/libgpg.c | 104 | ||||
| -rw-r--r-- | gpglib/list.c | 574 | ||||
| -rw-r--r-- | gpglib/mimegpg.c | 189 | ||||
| -rw-r--r-- | gpglib/mimegpg.sgml | 152 | ||||
| -rw-r--r-- | gpglib/mimegpgfork.c | 449 | ||||
| -rw-r--r-- | gpglib/mimegpgfork.h | 84 | ||||
| -rw-r--r-- | gpglib/mimegpgheader.c | 393 | ||||
| -rw-r--r-- | gpglib/mimegpgheader.h | 54 | ||||
| -rw-r--r-- | gpglib/mimegpgstack.c | 72 | ||||
| -rw-r--r-- | gpglib/mimegpgstack.h | 32 | ||||
| -rw-r--r-- | gpglib/options.c | 41 | ||||
| -rw-r--r-- | gpglib/rfc2045.c | 115 | ||||
| -rw-r--r-- | gpglib/sign.c | 109 | ||||
| -rw-r--r-- | gpglib/tempname.c | 77 | ||||
| -rw-r--r-- | gpglib/tempname.h | 23 | ||||
| -rw-r--r-- | gpglib/testgpg.c | 184 | ||||
| -rw-r--r-- | gpglib/webgpg.in | 39 | 
29 files changed, 6547 insertions, 0 deletions
| diff --git a/gpglib/.gitignore b/gpglib/.gitignore new file mode 100644 index 0000000..75404e5 --- /dev/null +++ b/gpglib/.gitignore @@ -0,0 +1,6 @@ +/gpg.h +/mimegpg +/mimegpg.1 +/mimegpg.html +/testgpg +/webgpg diff --git a/gpglib/Makefile.am b/gpglib/Makefile.am new file mode 100644 index 0000000..3ea6fbd --- /dev/null +++ b/gpglib/Makefile.am @@ -0,0 +1,45 @@ +# +# Copyright 2001-2007 Double Precision, Inc.  See COPYING for +# distribution information. +# + + +CLEANFILES=gpg.h + +noinst_SCRIPTS=webgpg + +noinst_LTLIBRARIES=libgpg.la +noinst_PROGRAMS=testgpg mimegpg + +libgpg_la_SOURCES=checksign.c delete.c export.c fork.c genkey.c gpg.c gpglib.h\ +	import.c libgpg.c list.c mimegpgfork.c mimegpgfork.h mimegpgheader.c \ +	mimegpgheader.h mimegpgstack.c mimegpgstack.h options.c rfc2045.c \ +	sign.c tempname.c tempname.h + +testgpg_SOURCES=testgpg.c +testgpg_LDADD=libgpg.la ../unicode/libunicode.la ../numlib/libnumlib.la +testgpg_DEPENDENCIES=$(testgpg_LDADD) +testgpg_LDFLAGS=-static + +mimegpg_SOURCES=mimegpg.c +mimegpg_LDADD=libgpg.la ../rfc2045/librfc2045.la ../rfc822/librfc822.la \ +		../rfc822/libencode.la ../numlib/libnumlib.la \ +		../unicode/libunicode.la +mimegpg_DEPENDENCIES=libgpg.la ../rfc2045/librfc2045.la ../rfc822/librfc822.la\ +		../rfc822/libencode.la ../numlib/libnumlib.la \ +		../unicode/libunicode.la +BUILT_SOURCES=gpg.h mimegpg.html mimegpg.1 +EXTRA_DIST=mimegpg.html mimegpg.1 README.html + +if HAVE_SGML + +mimegpg.html: mimegpg.sgml ../docbook/sgml2html +	../docbook/sgml2html mimegpg.sgml mimegpg.html + +mimegpg.1: mimegpg.sgml ../docbook/sgml2man +	../docbook/sgml2man mimegpg.sgml mimegpg.1 + +endif + +gpg.h: config.status +	echo '#define GPG "@GPG@"' >gpg.h diff --git a/gpglib/README.html b/gpglib/README.html new file mode 100644 index 0000000..a28d1a8 --- /dev/null +++ b/gpglib/README.html @@ -0,0 +1,357 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" +      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +  <title>GnuPG support in SqWebMail</title> +  <meta name="MSSmartTagsPreventParsing" content="TRUE" /> +</head> + +<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#551A8B" +alink="#FF0000" xml:lang="en" lang="en"> +<!-- Copyright 1998 - 2001 Double Precision, Inc.  See COPYING for --> +<!-- distribution information. --> + +<h1>GnuPG support in <code>SqWebMail</code></h1> + +<p>Table of contents:</p> +<ul> +  <li><a href="#intro">Introduction</a><br /> +    <br /> +  </li> +  <li><a href="#require">Requirements</a><br /> +    <br /> +  </li> +  <li><a href="#implementation">Implementation</a><br /> +    <br /> +  </li> +  <li><a href="#interop">Interoperability</a><br /> +    <br /> +  </li> +  <li><a href="#passphrase">Using passphrase-protected private keys</a><br /> +    <br /> +  </li> +  <li><a href="#bugs">Bugs</a><br /> +    <br /> +  </li> +  <li><a href="#random">Important note for Linux and other systems that use +    the <code>/dev/random</code> device.</a><br /> +    <br /> +  </li> +</ul> + +<h2><a name="intro" id="intro">Introduction</a></h2> + +<p>This is experimental beta of GnuPG support in <code>SqWebMail</code>. The +following functionality is available:</p> +<ul> +  <li>Create new keypairs<br /> +    <br /> +  </li> +  <li>List public/private keys<br /> +    <br /> +  </li> +  <li>Delete keys<br /> +    <br /> +  </li> +  <li>Sign keys<br /> +    <br /> +  </li> +  <li>Export keys (attach them to the current message)<br /> +    <br /> +  </li> +  <li>Import keys from messages<br /> +    <br /> +  </li> +  <li>Sign outgoing messages<br /> +    <br /> +  </li> +  <li>Encrypt outgoing messages<br /> +    <br /> +  </li> +  <li>Display and verify signed messages<br /> +    <br /> +  </li> +  <li>Decrypt encrypted messages<br /> +    <br /> +  </li> +  <li>Reply and forward encrypted messages.  Correctly handle +    multipart/alternative and multipart/related.<br /> +    <br /> +  </li> +</ul> + +<p><code>SqWebMail</code> creates and manages its own keyrings. +<code>$HOME/.gnupg</code> is not used. This allows GnuPG functions to work +for virtual accounts that have no home directory.</p> + +<h2><a name="require" id="require">Requirements</a></h2> + +<p>This implementation requires GnuPG, which is used for all of the heavy +lifting.  No encryption code exists in <code>SqWebMail</code> itself. The +configuration script searches the current PATH for the <code>gpg</code> +binary. The final <code>SqWebMail</code> binary gets the hardcoded absolute +path to the <code>gpg</code> binary. If <code>gpg</code> is not found in the +current PATH, <code>/usr/bin/gpg</code> will be used. Therefore, if GnuPG is +locally installed in a non-default location, make sure that <code>gpg</code> +can be found in the current PATH.</p> + +<h2><a name="implementation" id="implementation">Implementation</a></h2> + +<p><code><code>SqWebMail</code></code> runs <code>gpg</code> automatically to +handle all the actual tasks involved in encrypting and decrypting messages. +<code>make install</code> also installs two more binaries: +<code>reformime</code> and <code>mimegpg</code>. Those binaries do some other +things, and nothing really needs to be done about them, or to them, except to +know that they're there.</p> + +<h3>The <code>webgpg</code> script</h3> +<code>make install</code> automatically installs a shell script, +<code>webgpg</code>. Encryption/decryption capabilities for all mail accounts +are disabled by default. The <code>webgpg</code> script takes one argument - +the complete pathname to a Maildir. <code>webgpg</code> creates a new +subdirectory, <code>Maildir/gpg</code>, and initializes it. Once that +subdirectory is created, encryption/decryption in <code>SqWebMail</code> will +become available for that account. Not every mail account has to have +encryption/decryption enabled. This functionality can be selectively enabled +and disabled for individual accounts. + +<p>NOTE: <code>webgpg</code> script MUST be executed by the same userid and +groupid that owns the <code>Maildir</code> directory.</p> + +<p>NOTE: The current implementation consists of the bare minimum required to +get the overall functionality.  As a result, a number of shortcuts are taken +with respect to avoiding some hoops that GnuPG occasionally requires to be +jumped through.  For example, by default GnuPG requires that a public key +must be signed before it can be used for encryption.  <code>SqWebMail</code> +will automatically provide the "always-trust" option to bypass the check. +Basically, if you have the public key in your keyring, you can use it.</p> + +<h3>Issues with adding or activating encryption for existing accounts</h3> + +<p>Obviously, encryption/decryption takes additional server CPU cycles. There +is no hard and fast rule for how much additional load is needed. Fortunately, +this is not an "all or none" deal. Encryption support can be activated in a +controlled manner and phased in gradually, in steps, allowing the impact on +the web/mail server to be closely monitored, and controlled.</p> + +<h3>Issues with adding or activating encryption for new accounts</h3> + +<p>The <code>webgpg</code> script needs to be used to initialize encryption +support for newly-created accounts.  Many systems use the +<code>/etc/skel</code> directory as a template for setting up the initial +contents of new accounts.  In that case, use run "<code>webgpg +/etc/skel/Maildir</code>", and all new accounts will have encryption +automatically activated.</p> + +<h2><a name="interop" id="interop">Interoperability</a></h2> + +<p>As of now, <code>SqWebMail</code>'s encryption/decryption is experimental. +There's not been much interoperability testing with other encryption-capable +mail software.  It is very much possible that there are some interoperability +issues that will have to be worked out.</p> + +<p>The directory <code>Maildir/gpg</code> does pretty much what +<code>$HOME/.gnupg</code> does for the command-line <code>gpg</code> tool. +<code>SqWebMail</code> does not use <code>$HOME/.gnupg</code>, it keeps its +keyrings and other GnuPG-related stuff in the <code>Maildir/gpg</code> +directory. That's because <code>SqWebMail</code> can be configured to use +both physical system accounts and virtual mail accounts, and after +authenticating, there is no difference whatsoever between the two. Therefore, +<code>SqWebMail</code> has no concept of the $HOME directory. Its entire +universe consists of the <code>Maildir</code> directory, and its contents. +<code>SqWebMail</code> is completely unaware of the existence of +<code>$HOME/.gnupg</code>, and will never be aware of it. Any keypairs in +<code>$HOME/.gnupg</code> will have to be imported into +<code>Maildir/gpg</code>.</p> + +<h3>Importing keys</h3> + +<p>Since <code>$HOME/.gnupg</code> is not used, any existing keys will have +to be imported.  If shell access is available, the <code>pubring</code> and +<code>secring</code> files can simply be copied to <code>Maildir/gpg</code>. +The following procedure can be used to import keys in all other +circumstances. The following example imports keys from mail account M to +<code>SqWebMail</code> mail account W.</p> +<ul> +  <li>Create a temporary keypair in W.<br /> +    <br /> +  </li> +  <li>Create a new message, addressed to M, attach the new public key, and +    send it.<br /> +    <br /> +  </li> +  <li>In M, take the new public key and import it.<br /> +    <br /> +  </li> +  <li>Export M's secret key.  With GnuPG, use the following command: +    <pre>gpg --export-secret-key --armor [fingerprint]</pre> +    <p>The output from this command looks something like this:</p> +    <pre>-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.0.4 (GNU/Linux) +Comment: For info see http://www.gnupg.org + ++M3OSONvootZCzKXP6VT29Vf+lZLQkjSyuY28PpfflaFKi8YpeCEKo4dDhynxCbV +NAYk2I6wXguwl4MOT0ebkEWM1WS9lTsto7cCzz0ovSg3xe82PozA/4s6E5UUgl1B + +... +RQj/UASJxoPEEDHAhfZ0FYPsDFbo/P///4nDiTQk6Pz///+J2I1l+FteXcOJ9lWJ +RSBQ/3Uc/3UY/3UU/3UQ/3UM/3UI6GsAAACDxBz/dSxqBf91KGoAicP/dST8McCJ +-----END PGP PRIVATE KEY BLOCK-----</pre> +    <br /> +    <br /> +  </li> +  <li>Wrap this inside a MIME-formatted mail message.  The end result should +    look something like this: +    <pre>Mime-Version: 1.0 +Content-Type: multipart/mixed; boundary="xxx" + +--xxx +Content-Type: text/plain + +Here's the key + +--xxx +Content-Type: application/pgp-keys + +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.0.4 (GNU/Linux) +Comment: For info see http://www.gnupg.org + ++M3OSONvootZCzKXP6VT29Vf+lZLQkjSyuY28PpfflaFKi8YpeCEKo4dDhynxCbV +NAYk2I6wXguwl4MOT0ebkEWM1WS9lTsto7cCzz0ovSg3xe82PozA/4s6E5UUgl1B + +... +RQj/UASJxoPEEDHAhfZ0FYPsDFbo/P///4nDiTQk6Pz///+J2I1l+FteXcOJ9lWJ +RSBQ/3Uc/3UY/3UU/3UQ/3UM/3UI6GsAAACDxBz/dSxqBf91KGoAicP/dST8McCJ +-----END PGP PRIVATE KEY BLOCK----- + +--xxx</pre> +    <br /> +    <br /> +  </li> +  <li>Encrypt the whole message using W's public key.  The end result should +    look something like this: +    <pre>Mime-version: 1.0 +Content-Type: multipart/encrypted; +    boundary="yyy"; +    protocol="application/pgp-encrypted" + +--yyy +Content-Type: application/pgp-encrypted +Content-Transfer-Encoding: 7bit + +Version: 1 + +--yyy +Content-Type: application/octet-stream +Content-Transfer-Encoding: 7bit + +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.0.4 (GNU/Linux) +Comment: For info see http://www.gnupg.org + +hQIOA8nMaZ+WFjCiEAf+IzymijY0K0ELubJnDuTo0OZg6n8EApLTdwXVZ7sjoWvU +YbEjfvwBXp+wc7X4lvw3S/AGYe4/mmshgTnRjuDPKsN3zHViYGdGF0UaB40sjUp3 + +... +U9Qi2kvxRvV0b8nBN1qeFUSSRF8Ncwug1sr/rFVNNWA0yavb5WDFrqRE6WFJU3z3 +p0vPPRbdWCOTP7WGOygpHdRMLQSCxG4m+L9uG3roG5Uu7A== +=GutI +-----END PGP MESSAGE----- + +--yyy--</pre> +    <br /> +  </li> +  <li>Mail this message to W.  Repeat the procedure with M's public key +    (<code>gpg --export-key --armor).  If you're handy with composing MIME +    messages, both keys can be sent in a single encrypted message. <br /> +    <br /> +    </code></li> +  <li>In <code>SqWebMail</code>, you should be able to decrypt the message +    containing the keys, and import them.</li> +</ul> + +<h2><a name="passphrase" id="passphrase">Using passphrase-protected private +keys</a></h2> + +<p>SqWebMail can handle passphrase-protected keys only when SSL is being +used. This is an artificial restriction that prevents passphrases from going +over the network, in the clear. When SSL is used, SqWebMail will +automatically prompt for a passphrase. Leave the passphrase field empty if +the secret key is not protected by a passphrase.</p> + +<h2><a name="bugs" id="bugs">Bugs</a></h2> +<ol> +  <li>Although <code>SqWebMail</code> contains mappings for many charsets, +    GnuPG versions prior to 1.0.6 know only about <code>iso-8859-1</code> and +    <code>iso-8859-2</code>. All messages displayed by <code>SqWebMail</code> +    from GnuPG 1.0.5 or earlier will come out in <code>iso-8859-1</code>. <br +    /> +    <br /> +  </li> +  <li>Currently, there are no command line options in GnuPG for removing key +    signatures (is that even possible?).  You can sign keys, but can't +    "unsign" them.<br /> +    <br /> +  </li> +  <li>There has not been any/much interoperability testing with other +    GnuPG/PGP mail software.  It is entirely possible that +    <code>SqWebMail</code>'s implementation is currently not interoperable, +    and may require some tinkering as time goes by.<br /> +    <br /> +  </li> +  <li>GnuPG will always blather "Warning: secret key NOT protected" when +    using keys not protected by a passphrase. This whinge is harmless, and +    can be ignored.<br /> +    <br /> +  </li> +  <li>The error message from GnuPG for a missing passphrase is not very +    helpful: "Unable to get tty input". This is because SqWebMail runs GnuPG +    with the --no-tty flag, and provides a passphrase separately, when it's +    entered. When SqWebMail does not provide a passphrase, GnuPG complains +    that tty input is disabled.<br /> +    <br /> +  </li> +</ol> + +<h2><a name="random" id="random">Important note for Linux and other systems +that use the <code>/dev/random</code> device.</a></h2> + +<p>If GnuPG is compiled on a system that has a <code>/dev/random</code> +device, GnuPG will use the <code>/dev/random</code> device as a source for +random entropy. The <code>/dev/random</code> device driver in Linux, and in +many other implementations, creates the entropy pool from system device +driver background activity. Unfortunately, it's is not a bottomless pit of +random entropy. When the random device driver entropy pool is empty, +<code>/dev/random</code> halts until device drivers create more noise.</p> + +<p>That means that when GnuPG is used on Linux, GnuPG may pause for an +excessive period of time, and issue its famous "please do something" prompt. +Since GnuPG is invoked in automatic mode, there is no other alternative, +except to wait until more device driver noise becomes available.</p> + +<p>This is more likely to happen when GnuPG is used to create new keypairs, +especially ones that are 1024 bits, or longer. Before deploying, extensive +testing is recommended to make sure that even an idle system generates +sufficient background noise so that the additional wait is not excessive. +There are several possible solutions that can also be used:</p> +<ol> +  <li>GnuPG can be optionally configured to use a pseudo-random generator +    that does not use <code>/dev/random</code>, but instead uses several +    alternative sources of random junk. Consult GnuPG's documentation for +    more information.<br /> +    <br /> +  </li> +  <li>Many systems also have a <code>/dev/urandom</code> device, which +    automatically generates pseudo-random noise if <code>/dev/random</code> +    runs out of entropy. This makes it possible to simply remove the +    <code>/dev/random</code> device and create a link from +    <code>/dev/random</code> to <code>/dev/urandom</code>.</li> +</ol> + +<p>Although - technically - both approaches can theoretically result in +slightly less secure keypairs, practically the difference is probably +academic in nature.</p> +</body> +</html> diff --git a/gpglib/checksign.c b/gpglib/checksign.c new file mode 100644 index 0000000..a5333ba --- /dev/null +++ b/gpglib/checksign.c @@ -0,0 +1,75 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + + +/* +** Check signature. +*/ + +static int dochecksign(int (*)(const char *, size_t, void *), +		    void *); + +int libmail_gpg_checksign(const char *gpgdir, const char *content, +		  const char *signature, +		  int (*dump_func)(const char *, size_t, void *), +		  void *voidarg) +{ +	char *argvec[10]; +	int rc; + +	argvec[0]="gpg"; +	argvec[1]="--no-tty"; +	argvec[2]="--verify"; +	argvec[3]=(char *)signature; +	argvec[4]=(char *)content; +	argvec[5]=0; + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, +			     &libmail_gpg_stderr, gpgdir, argvec) < 0) +		rc= -1; +	else +	{ +		int rc2; + +		rc=dochecksign(dump_func, voidarg); +		rc2=libmail_gpg_cleanup(); +		if (rc2) +			rc=rc2; +	} +	return (rc); +} + +static int dochecksign(int (*dump_func)(const char *, size_t, void *), +		    void *voidarg) +{ +	int rc=libmail_gpg_write("Y\n", 2, dump_func, NULL, NULL, 0, voidarg); +	int rc2; + +	if (rc == 0) +		rc=libmail_gpg_read(dump_func, dump_func, NULL, 0, voidarg); +	rc2=libmail_gpg_cleanup(); +	if (rc == 0) +		rc=rc2; +	return (rc); +} diff --git a/gpglib/configure.in b/gpglib/configure.in new file mode 100644 index 0000000..80b00aa --- /dev/null +++ b/gpglib/configure.in @@ -0,0 +1,120 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl Copyright 2001-2010 Double Precision, Inc.  See COPYING for +dnl distribution information. +dnl + +AC_INIT(webpgp, 0.10, [courier-users@lists.sourceforge.net]) + +>confdefs.h  # Kill PACKAGE_ macros + +AC_CONFIG_SRCDIR(webgpg.in) +AC_CONFIG_AUX_DIR(../..) +AM_INIT_AUTOMAKE([foreign no-define]) +LPATH="$PATH:/usr/local/bin" + +AM_CONFIG_HEADER(config.h) + +dnl Checks for programs. +AC_PROG_AWK +AC_PROG_INSTALL +AC_PROG_CC +AC_LIBTOOL_DLOPEN +AM_PROG_LIBTOOL +AC_PROG_LN_S +AC_PATH_PROGS(GPG, gpg gpg2, /usr/bin/gpg, $LPATH) +AC_PATH_PROGS(PERL, perl5 perl, perl, $LPATH) + +if test "$PERL" = "perl" +then +        AC_MSG_ERROR(Perl is required) +fi + +AC_CACHE_CHECK( [for gpg charset], ac_cv_gpg_charset, + +rm -rf conftestdir +mkdir conftestdir + +GNUPGHOME=`pwd`/conftestdir +export GNUPGHOME + +if $GPG --charset utf-8 --list-keys >/dev/null 2>&1 +then +	ac_cv_gpg_charset=utf-8 +else +	ac_cv_gpg_charset=iso-8859-1 +fi +rm -rf conftestdir +unset GNUPGHOME + +) + +AC_DEFINE_UNQUOTED(GPG_CHARSET,"$ac_cv_gpg_charset", +	[ Default gpg output character set ]) + +VERSION="`$GPG --version | sed '2,$d;s/.* //'`" + +if test "$VERSION" = "" +then +	AC_MSG_WARN(Unable to determine gpg version) +else +	has_cert_check_level=1 + +	case $VERSION in +	1.0.4) +		has_cert_check_level=0 +		;; +	1.0.5) +		has_cert_check_level=0 +		;; +	1.0.6) +		has_cert_check_level=0 +		;; +	esac + +fi + +if test "$has_cert_check_level" = 1 +then +	AC_DEFINE_UNQUOTED(GPG_HAS_CERT_CHECK_LEVEL,1, +	[ Whether gpg --sign-key asks for certificate trust level ]) +fi + +rm -rf conftempdir +mkdir conftempdir + +if $GPG --homedir conftempdir --list-keys --allow-secret-key-import >/dev/null +then +	AC_DEFINE_UNQUOTED(GPG_HAS_ALLOW_SECRET_KEY_IMPORT,1, +	[ Whether gpg has the --allow-secret-key-import option ]) +fi + +rm -rf conftempdir + +dnl Checks for libraries. + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h sys/wait.h sys/time.h unistd.h fcntl.h) +AC_HEADER_SYS_WAIT +AC_HEADER_TIME + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_SYS_LARGEFILE + +dnl Checks for library functions. +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(gettimeofday) + +if test "$GCC" = yes ; then +        CFLAGS="$CFLAGS -Wall" +fi + +CFLAGS="-I.. -I$srcdir/.. $CFLAGS" + +AM_CONDITIONAL(HAVE_SGML, test -d ${srcdir}/../docbook) + +AC_OUTPUT(Makefile webgpg) diff --git a/gpglib/delete.c b/gpglib/delete.c new file mode 100644 index 0000000..53943c9 --- /dev/null +++ b/gpglib/delete.c @@ -0,0 +1,77 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + + +/* +** Delete a key. +*/ + +static int dodeletekey(int (*)(const char *, size_t, void *), +		       void *); + +int libmail_gpg_deletekey(const char *gpgdir, int secret, +			  const char *fingerprint, +			  int (*dump_func)(const char *, size_t, void *), +			  void *voidarg) +{ +	char *argvec[8]; +	int rc; + +	argvec[0]="gpg"; +	argvec[1]="--command-fd"; +	argvec[2]="0"; +	argvec[3]= secret ? "--delete-secret-key":"--delete-key"; +	argvec[4]="-q"; +	argvec[5]="--no-tty"; +	argvec[6]=(char *)fingerprint; +	argvec[7]=0; + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL, +			     gpgdir, argvec) < 0) +		rc= -1; +	else +	{ +		int rc2; + +		rc=dodeletekey(dump_func, voidarg); +		rc2=libmail_gpg_cleanup(); +		if (rc2) +			rc=rc2; +	} +	return (rc); +} + +static int dodeletekey(int (*dump_func)(const char *, size_t, void *), +		    void *voidarg) +{ +	int rc=libmail_gpg_write("Y\n", 2, dump_func, NULL, NULL, 0, voidarg); +	int rc2; + +	if (rc == 0) +		rc=libmail_gpg_read(dump_func, NULL, NULL, 0, voidarg); +	rc2=libmail_gpg_cleanup(); +	if (rc == 0) +		rc=rc2; +	return (rc); +} diff --git a/gpglib/export.c b/gpglib/export.c new file mode 100644 index 0000000..66a4a05 --- /dev/null +++ b/gpglib/export.c @@ -0,0 +1,64 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<signal.h> +#include	<errno.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + +/* +** List keys +*/ + +int libmail_gpg_exportkey(const char *gpgdir, +		  int secret, +		  const char *fingerprint, +		  int (*out_func)(const char *, size_t, void *), +		  int (*err_func)(const char *, size_t, void *), +		  void *voidarg) +{ +	char *argvec[6]; +	int rc; + +	argvec[0]="gpg"; +	argvec[1]="--armor"; +	argvec[2]="--no-tty"; +	argvec[3]= secret ? "--export-secret-keys":"--export"; +	argvec[4]=(char *)fingerprint; +	argvec[5]=0; + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, +			     &libmail_gpg_stderr, gpgdir, argvec) < 0) +		rc= -1; +	else +	{ +		int rc2; + +		close(libmail_gpg_stdin); +		libmail_gpg_stdin=-1; + +		rc=libmail_gpg_read(out_func, err_func, NULL, 0, voidarg); +		rc2=libmail_gpg_cleanup(); +		if (rc2) +			rc=rc2; +	} +	return (rc); +} diff --git a/gpglib/fork.c b/gpglib/fork.c new file mode 100644 index 0000000..b44c0ac --- /dev/null +++ b/gpglib/fork.c @@ -0,0 +1,359 @@ +/* +** Copyright 2001-2006 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<signal.h> +#include	<errno.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#include	"gpg.h" +#include	"gpglib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + + +/* +** Helper function: for and run pgp, with the given file descriptors and +** options. +*/ + +pid_t libmail_gpg_fork(int *libmail_gpg_stdin, int *libmail_gpg_stdout, +		       int *libmail_gpg_stderr, +		       const char *gpgdir, +		       char **argvec) +{ +	int pipein[2], pipeout[2], pipeerr[2]; +	pid_t p; +	char *s; + +	if (libmail_gpg_stdin && pipe(pipein) < 0) +		return (-1); + +	if (libmail_gpg_stdout && pipe(pipeout) < 0) +	{ +		if (libmail_gpg_stdin) +		{ +			close(pipein[0]); +			close(pipein[1]); +		} +		return (-1); +	} + +	if (libmail_gpg_stderr && pipe(pipeerr) < 0) +	{ +		if (libmail_gpg_stdout) +		{ +			close(pipeout[0]); +			close(pipeout[1]); +		} + +		if (libmail_gpg_stdin) +		{ +			close(pipein[0]); +			close(pipein[1]); +		} +		return (-1); +	} + +	signal(SIGCHLD, SIG_DFL); +	p=libmail_gpg_pid=fork(); +	if (p < 0) +	{ +		if (libmail_gpg_stderr) +		{ +			close(pipeerr[0]); +			close(pipeerr[1]); +		} + +		if (libmail_gpg_stdout) +		{ +			close(pipeout[0]); +			close(pipeout[1]); +		} +		if (libmail_gpg_stdin) +		{ +			close(pipein[0]); +			close(pipein[1]); +		} + +		return (-1); +	} + +	if (p) +	{ +		signal(SIGPIPE, SIG_IGN); + +		if (libmail_gpg_stderr) +		{ +			close(pipeerr[1]); +			*libmail_gpg_stderr=pipeerr[0]; +		} + +		if (libmail_gpg_stdout) +		{ +			close(pipeout[1]); +			*libmail_gpg_stdout=pipeout[0]; +		} + +		if (libmail_gpg_stdin) +		{ +			close(pipein[0]); +			*libmail_gpg_stdin=pipein[1]; +		} +		return (0); +	} + +	if (libmail_gpg_stderr) +	{ +		dup2(pipeerr[1], 2); +		close(pipeerr[0]); +		close(pipeerr[1]); +	} +	else if (libmail_gpg_stdout) +	{ +		dup2(pipeout[1], 2); +	} + +	if (libmail_gpg_stdout) +	{ +		dup2(pipeout[1], 1); +		close(pipeout[0]); +		close(pipeout[1]); +	} + +	if (libmail_gpg_stdin) +	{ +		dup2(pipein[0], 0); +		close(pipein[0]); +		close(pipein[1]); +	} + +	if (gpgdir) +	{ +		s=malloc(sizeof("GNUPGHOME=")+strlen(gpgdir)); +		if (!s) +		{ +			perror("malloc"); +			exit(1); +		} +		strcat(strcpy(s, "GNUPGHOME="), gpgdir); +		if (putenv(s) < 0) +		{ +			perror("putenv"); +			exit(1); +		} +	} + +	{ +		const char *gpg=getenv("GPG"); +		if (!gpg || !*gpg) +			gpg=GPG; + +		execv(gpg, argvec); +		perror(gpg); +	} +	_exit(1); +	return (0); +} + +int libmail_gpg_write(const char *p, size_t cnt, +		      int (*stdout_func)(const char *, size_t, void *), +		      int (*stderr_func)(const char *, size_t, void *), +		      int (*timeout_func)(void *), +		      unsigned timeout, +		      void *voidarg) +{ +	char buf[BUFSIZ]; + +	fd_set fdr, fdw; +	struct timeval tv; + +	if (!timeout_func) +		timeout=0; +	while (cnt) +	{ +		int maxfd=0; +		int n; + +		FD_ZERO(&fdr); +		FD_ZERO(&fdw); + +		FD_SET(libmail_gpg_stdin, &fdw); + +		if (libmail_gpg_stdout >= 0) +		{ +			FD_SET(libmail_gpg_stdout, &fdr); +			if (libmail_gpg_stdout > maxfd) +				maxfd=libmail_gpg_stdout; +		} + +		if (libmail_gpg_stderr >= 0) +		{ +			FD_SET(libmail_gpg_stderr, &fdr); +			if (libmail_gpg_stderr > maxfd) +				maxfd=libmail_gpg_stderr; +		} + +		tv.tv_usec=0; +		tv.tv_sec=timeout; +		n=select(maxfd+1, &fdr, &fdw, NULL, timeout ? &tv:NULL); +		if (n == 0) +		{ +			n=(*timeout_func)(voidarg); +			if (n) +				return(n); +			continue; +		} +		if (n < 0) +			continue; + +		if (FD_ISSET(libmail_gpg_stdin, &fdw)) +		{ +			int n=write(libmail_gpg_stdin, p, cnt); + +			if (n <= 0) +				return (-1); + +			p += n; +			cnt -= n; +		} + +		if (libmail_gpg_stdout >= 0 && +		    FD_ISSET(libmail_gpg_stdout, &fdr)) +		{ +			int n=read(libmail_gpg_stdout, buf, sizeof(buf)); + +			if (n <= 0) +			{ +				close(libmail_gpg_stdout); +				libmail_gpg_stdout= -1; +			} +			else if (stdout_func && +				 (n=(*stdout_func)(buf, n, voidarg)) != 0) +				return (n); +		} + +		if (libmail_gpg_stderr >= 0 && +		    FD_ISSET(libmail_gpg_stderr, &fdr)) +		{ +			int n=read(libmail_gpg_stderr, buf, sizeof(buf)); + +			if (n <= 0) +			{ +				close(libmail_gpg_stderr); +				libmail_gpg_stderr= -1; +			} +			else if (stderr_func && +				 (n=(*stderr_func)(buf, n, voidarg)) != 0) +				return (n); +		} +	} +	return (0); +} + +int libmail_gpg_read(int (*stdout_func)(const char *, size_t, void *), +		     int (*stderr_func)(const char *, size_t, void *), +		     int (*timeout_func)(void *), +		     unsigned timeout, +		     void *voidarg) +{ +	char buf[BUFSIZ]; + +	fd_set fdr; +	struct timeval tv; + +	if (libmail_gpg_stdin >= 0) +	{ +		close(libmail_gpg_stdin); +		libmail_gpg_stdin= -1; +	} + +	if (!timeout_func) +		timeout=0; + +	while ( libmail_gpg_stdout >= 0 || libmail_gpg_stderr >= 0) +	{ +		int maxfd=0; +		int n; + +		FD_ZERO(&fdr); + +		if (libmail_gpg_stdout >= 0) +		{ +			FD_SET(libmail_gpg_stdout, &fdr); +			if (libmail_gpg_stdout > maxfd) +				maxfd=libmail_gpg_stdout; +		} + +		if (libmail_gpg_stderr >= 0) +		{ +			FD_SET(libmail_gpg_stderr, &fdr); +			if (libmail_gpg_stderr > maxfd) +				maxfd=libmail_gpg_stderr; +		} + +		tv.tv_usec=0; +		tv.tv_sec=timeout; +		n=select(maxfd+1, &fdr, NULL, NULL, timeout ? &tv:NULL); + +		if (n == 0) +		{ +			n=(*timeout_func)(voidarg); +			if (n) +				return(n); +			continue; +		} +		if (n < 0) +			continue; + +		if (libmail_gpg_stdout >= 0 && +		    FD_ISSET(libmail_gpg_stdout, &fdr)) +		{ +			int n=read(libmail_gpg_stdout, buf, sizeof(buf)); + +			if (n <= 0) +			{ +				close(libmail_gpg_stdout); +				libmail_gpg_stdout= -1; +			} +			else if (stdout_func && +				 (n=(*stdout_func)(buf, n, voidarg)) != 0) +				return (n); +		} + +		if (libmail_gpg_stderr >= 0 && +		    FD_ISSET(libmail_gpg_stderr, &fdr)) +		{ +			int n=read(libmail_gpg_stderr, buf, sizeof(buf)); + +			if (n <= 0) +			{ +				close(libmail_gpg_stderr); +				libmail_gpg_stderr= -1; +			} +			else if (stderr_func && +				 (n=(*stderr_func)(buf, n, voidarg)) != 0) +				return (n); +		} +	} +	return (0); +} diff --git a/gpglib/genkey.c b/gpglib/genkey.c new file mode 100644 index 0000000..9e72f1c --- /dev/null +++ b/gpglib/genkey.c @@ -0,0 +1,209 @@ +/* +** Copyright 2001-2011 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<signal.h> +#include	<errno.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + + +/* +** Generate a new key. +*/ + +static int dogenkey(const char *, const char *, const char *, +		    int, +		    int, +		    unsigned, +		    char, +		    const char *passphrase, +		    int (*)(const char *, size_t, void *), +		    int (*)(void *), +		    void *); + +int libmail_gpg_genkey(const char *gpgdir, +		       const char *charset, +		       const char *name, +		       const char *addr, +		       const char *comment, +		       int skeylen, +		       int ekeylen, +		       unsigned expire, +		       char expire_unit, +		       const char *passphrase, +		       int (*dump_func)(const char *, size_t, void *), +		       int (*timeout_func)(void *), +		       void *voidarg) +{ +	char *name_u, *addr_u, *comment_u; +	char *argvec[4]; +	int rc; + +	name_u=libmail_u_convert_toutf8(name, charset, NULL); + +	if (!name_u) +		return (-1); + +	addr_u=libmail_u_convert_toutf8(addr, charset, NULL); +	if (!addr_u) +	{ +		free(name_u); +		return (-1); +	} + +	comment_u=libmail_u_convert_toutf8(comment, charset, NULL); +	if (!comment_u) +		return (-1); + + +	argvec[0]="gpg"; +	argvec[1]="--gen-key"; +	argvec[2]="--batch"; +	argvec[3]=NULL; + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL, +			     gpgdir, argvec) < 0) +		rc= -1; +	else +	{ +		int rc2; + +		rc=dogenkey(name_u, addr_u, comment_u, +			    skeylen, ekeylen, expire, expire_unit, +			    passphrase, +			    dump_func, timeout_func, voidarg); +		rc2=libmail_gpg_cleanup(); +		if (rc2) +			rc=rc2; +	} +	free(comment_u); +	free(name_u); +	free(addr_u); +	return (rc); +} + +static char *mkcmdbuf(const char *, const char *, const char *, +		      int, +		      int, +		      unsigned, +		      char, const char *); + +static int dogenkey(const char *name, const char *addr, const char *comment, +		    int skeylen, +		    int ekeylen, +		    unsigned expire, +		    char expire_unit, +		    const char *passphrase, +		    int (*dump_func)(const char *, size_t, void *), +		    int (*timeout_func)(void *), +		    void *voidarg) +{ +	char *cmd_buf=mkcmdbuf(name, addr, comment, skeylen, ekeylen, +			       expire, expire_unit, passphrase); + +	int rc=libmail_gpg_write(cmd_buf, strlen(cmd_buf), dump_func, NULL, +			 timeout_func, 5, voidarg); +	int rc2; + +	free(cmd_buf); +	if (rc == 0) +		rc=libmail_gpg_read(dump_func, NULL, timeout_func, 5, voidarg); +	rc2=libmail_gpg_cleanup(); +	if (rc == 0) +		rc=rc2; +	return (rc); +} + +static char *mkcmdbuf(const char *name, const char *addr, const char *comment, +		      int skeylen, +		      int ekeylen, +		      unsigned expire, +		      char expire_unit, +		      const char *passphrase) +{ +	static const char genkey_cmd[]= +		"Key-Type: DSA\n" +		"Key-Length: %s\n" +		"Subkey-Type: ELG-E\n" +		"Subkey-Length: %s\n" +		"%s%s%s" +		"%s%s%s" +		"%s%s%s" +		"Name-Email: %s\n" +		"Expire-Date: %s\n" +		"%%commit\n"; + +	static const char namereal1[]="Name-Real: "; +	static const char namereal2[]="\n"; +	static const char comment1[]="Name-Comment: "; +	static const char comment2[]="\n"; +	static const char passphrase1[]="Passphrase: "; +	static const char passphrase2[]="\n"; + +	char skl_buf[NUMBUFSIZE]; +	char kl_buf[NUMBUFSIZE]; +	char exp_buf[NUMBUFSIZE+1]; + +	char *p; + +	libmail_str_size_t(skeylen, skl_buf); +	libmail_str_size_t(ekeylen, kl_buf); +	if (expire == 0) +	{ +		strcpy(exp_buf, "0"); +	} +	else +	{ +		char b[2]; + +		b[0]=expire_unit; +		b[1]=0; + +		strcat(libmail_str_size_t(expire, exp_buf), b); +	} + +	p=malloc(512+strlen(kl_buf)+strlen(skl_buf)+strlen(exp_buf) +		 +strlen(name)+strlen(addr)+strlen(comment) +		 +strlen(passphrase)); + +	if (!p) +		return (NULL); + +	while (*comment == ' ' || *comment == '\t') +		++comment; + +	sprintf(p, genkey_cmd, skl_buf, kl_buf, + +		*name ? namereal1:"", +		name, +		*name ? namereal2:"", + +		*comment ? comment1:"", +		comment, +		*comment ? comment2:"", + +		*passphrase ? passphrase1:"", +		passphrase, +		*passphrase ? passphrase2:"", + +		addr, exp_buf); +	return (p); +} diff --git a/gpglib/gpg.c b/gpglib/gpg.c new file mode 100644 index 0000000..b63214c --- /dev/null +++ b/gpglib/gpg.c @@ -0,0 +1,2235 @@ +/* +** Copyright 2001-2011 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#if	HAVE_FCNTL_H +#include <fcntl.h> +#endif +#include <sys/types.h> +#if	HAVE_SYS_TIME_H +#include	<sys/time.h> +#endif +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#include "mimegpgheader.h" +#include "mimegpgstack.h" +#include "mimegpgfork.h" +#include "tempname.h" +#include "gpglib.h" +#include "rfc822/encode.h" +#include "rfc2045/rfc2045.h" + +static int my_rewind(FILE *fp) +{ +	if (fflush(fp) || ferror(fp) || fseek(fp, 0L, SEEK_SET)) +		return (-1); +	clearerr(fp); +	return (0); +} + +int libmail_gpg_inputfunc_readfp(char *buf, size_t cnt, void *vp) +{ +	FILE *fp=(FILE *)vp; +	size_t i; +	int c; + +	if (cnt == 0) +		return -1; + +	--cnt; + +	for (i=0; i<cnt; i++) +	{ +		if ((c=getc(fp)) == EOF) +		{ +			if (i == 0) +				return -1; +			break; +		} +		buf[i]=c; + +		if (c == '\n') +		{ +			++i; +			break; +		} +	} +	buf[i]=0; +	return 0; +} + +void libmail_gpg_noexec(int fd) +{ +#ifdef FD_CLOEXEC +	fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif +} + +/* +** Check if the line just read is a MIME boundary line.  Search the +** current MIME stack for a matching MIME boundary delimiter. +*/ + +static struct mimestack *is_boundary(struct mimestack *s, const char *line, +				     int *isclosing) +{ +	struct mimestack *b; + +	if (line[0] != '-' || line[1] != '-' || +	    (b=libmail_mimestack_search(s, line+2)) == 0) +		return (NULL); + + +	*isclosing=strncmp(line+2+strlen(b->boundary), "--", 2) == 0; +	return (b); +} + +static const char *get_boundary(struct mimestack *, +				const char *, +				FILE *); + +/* +** Skip until EOF or a MIME boundary delimiter other than a closing MIME +** boundary delimiter.  After returning from bind_boundary we expect to +** see MIME headers.  Copy any intermediate lines to fpout. +*/ + +static void find_boundary(struct mimestack **stack, int *iseof, +			  int (*input_func)(char *, size_t, void *vp), +			  void *input_func_arg, +			  void (*output_func)(const char *, +					      size_t, +					      void *), +			  void *output_func_arg, +			  int doappend) +{ +	char buf[BUFSIZ]; + +	for (;;) +	{ +		int is_closing; +		struct mimestack *b; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			return; +		} + +		if (!(b=is_boundary(*stack, buf, &is_closing))) +		{ +			if (output_func) +				(*output_func)(buf, strlen(buf), +					       output_func_arg); + +			while (strchr(buf, '\n') == 0) +			{ +				if ( (*input_func)(buf, sizeof(buf), +						   input_func_arg)) +				{ +					*iseof=1; +					return; +				} +				if (output_func) +					(*output_func)(buf, strlen(buf), +						       output_func_arg); +			} +			continue; +		} + +		if (output_func) +		{ +			(*output_func)("--", 2, output_func_arg); +			(*output_func)(b->boundary, strlen(b->boundary), +				       output_func_arg); + +			if (is_closing) +				(*output_func)("--", 2, output_func_arg); + +			(*output_func)("\n", 1, output_func_arg); +		} + +		if (is_closing) +		{ +			libmail_mimestack_pop_to(stack, b); +			continue; +		} +		break; +	} +} + + +/* +** Read a set of headers. +*/ + +static struct header *read_headers(struct mimestack **stack, int *iseof, +				   int (*input_func)(char *, size_t, void *vp), +				   void *input_func_arg, +				   void (*output_func)(const char *, +						       size_t, +						       void *), +				   void *output_func_arg, +				   int doappend, +				   int *errflag) +{ +	char buf[BUFSIZ]; +	struct read_header_context rhc; +	struct header *h; + +	*errflag=0; +	libmail_readheader_init(&rhc); + +	while (!*iseof) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (READ_START_OF_LINE(rhc)) +		{ +			struct mimestack *b; +			int is_closing; + +			if (strcmp(buf, "\n") == 0 +			    || strcmp(buf, "\r\n") == 0) +				break; + +			b=is_boundary(*stack, buf, &is_closing); + +			if (b) +			{ +				/* +				** Corrupted MIME message.  We should NOT +				** see a MIME boundary in the middle of the +				** headers! +				** +				** Ignore this damage. +				*/ + +				struct header *p; + +				h=libmail_readheader_finish(&rhc); + +				for (p=h; p; p=p->next) +					(*output_func)(p->header, +						       strlen(p->header), +						       output_func_arg); + +				(*output_func)("--", 2, output_func_arg); +				(*output_func)(b->boundary, +					       strlen(b->boundary), +					       output_func_arg); + +				if (is_closing) +					(*output_func)("--", 2, +						       output_func_arg); + +				(*output_func)("\n", 1, output_func_arg); + +				if (is_closing) +				{ +					libmail_mimestack_pop_to(stack, b); +					find_boundary(stack, iseof, +						      input_func, +						      input_func_arg, +						      output_func, +						      output_func_arg, +						      doappend); +				} +				libmail_header_free(h); + +				libmail_readheader_init(&rhc); +				continue; /* From the top */ +			} +		} +		if (libmail_readheader(&rhc, buf) < 0) +		{ +			libmail_header_free(libmail_readheader_finish(&rhc)); +			*errflag= -1; +			return NULL; +		} +	} + +	return (libmail_readheader_finish(&rhc)); +} + +/* +** Here we do actual signing/encoding +*/ + +static int encode_header(const char *h) +{ +	if (strncasecmp(h, "content-", 8) == 0) +		return (1); +	return (0); +} + +struct gpg_fork_output_info { +	void (*output_func)(const char *, size_t, void *); +	void *output_func_arg; +	struct gpgmime_forkinfo *gpgptr; +}; + +static int gpg_fork_output(const char *p, size_t n, void *dummy) +{ +	struct gpg_fork_output_info *info=(struct gpg_fork_output_info *)dummy; + +	(*info->output_func)(p, n, info->output_func_arg); +	return 0; +} + + +static int dogpgencrypt(const char *gpghome, +			const char *passphrase_fd, +			struct mimestack **stack, +			struct header *h, int *iseof, +			int (*input_func)(char *, size_t, void *vp), +			void *input_func_arg, +			void (*output_func)(const char *, +					    size_t, +					    void *), +			void *output_func_arg, +			int argc, +			char **argv, +			int dosign, +			void (*errhandler)(const char *, void *), +			void *errhandler_arg) +{ +	struct header *hp; +	char buf[BUFSIZ]; +	struct gpgmime_forkinfo gpg; +	int clos_flag=0; +	struct mimestack *b=0; +	int rc; +	const char *boundary; +	int need_crlf; +	struct gpg_fork_output_info gfoi; + +	boundary=get_boundary(*stack, "", NULL); + +	gfoi.output_func=output_func; +	gfoi.output_func_arg=output_func_arg; + +	if (libmail_gpgmime_forksignencrypt(gpghome, passphrase_fd, +					    (dosign ? GPG_SE_SIGN:0) +					    | GPG_SE_ENCRYPT, +					    argc, argv, +					    &gpg_fork_output, &gfoi, +					    &gpg)) +	{ +		return -1; +	} + +	for (hp=h; hp; hp=hp->next) +	{ +		if (encode_header(hp->header)) +			continue; + +		(*output_func)(hp->header, strlen(hp->header), +			       output_func_arg); +	} + +#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg) +#define S(s) (*output_func)( s, strlen(s), output_func_arg) + + +	C("Content-Type: multipart/encrypted;\n" +	  "    boundary=\""); + +	S(boundary); + +	C("\";\n" +	  "    protocol=\"application/pgp-encrypted\"\n" +	  "\n" +	  "This is a MIME GnuPG-encrypted message.  If you see this text, it means\n" +	  "that your E-mail or Usenet software does not support MIME encrypted messages.\n" +	  "The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.\n" +	  "To open this message correctly you will need to install E-mail or Usenet\n" +	  "software that supports modern Internet standards.\n" +	  "\n--"); + +	S(boundary); + +	C("\n" +	  "Content-Type: application/pgp-encrypted\n" +	  "Content-Transfer-Encoding: 7bit\n" +	  "\n" +	  "Version: 1\n" +	  "\n--"); + +	S(boundary); + +	C("\n" +	  "Content-Type: application/octet-stream\n" +	  "Content-Transfer-Encoding: 7bit\n\n"); + +#undef C +#undef S + +	/* For Eudora compatiblity */ +        libmail_gpgmime_write(&gpg, "Mime-Version: 1.0\r\n", 19); + +	for (hp=h; hp; hp=hp->next) +	{ +		const char *p; + +		if (!encode_header(hp->header)) +			continue; + +		for (p=hp->header; *p; p++) +		{ +			if (*p == '\r') +				continue; + +			if (*p == '\n') +				libmail_gpgmime_write(&gpg, "\r\n", 2); +			else +				libmail_gpgmime_write(&gpg, p, 1); +		} +	} + +	/* +	** Chew the content until the next MIME boundary. +	*/ +	need_crlf=1; + +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (need_crlf) +		{ +			if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL) +				break; + +			libmail_gpgmime_write(&gpg, "\r\n", 2); +		} + +		need_crlf=0; +		for (;;) +		{ +			for (p=buf; *p; p++) +			{ +				if (*p == '\r') +					continue; +				if (*p == '\n') +				{ +					need_crlf=1; +					break; +				} + +				libmail_gpgmime_write(&gpg, p, 1); +			} +			if (*p == '\n') +				break; + +			if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +			{ +				*iseof=1; +				break; +			} +		} +	} + +	/* +	** This needs some 'splainin.  Note that we spit out a newline at +	** the BEGINNING of each line, above.  This generates the blank +	** header->body separator line.  Now, if we're NOT doing multiline +	** content, we need to follow the last line of the content with a +	** newline.  If we're already doing multiline content, that extra +	** newline (if it exists) is already there. +	*/ + +	if (!*stack) +	{ +		libmail_gpgmime_write(&gpg, "\r\n", 2); +	} + +	rc=libmail_gpgmime_finish(&gpg); + +	if (rc) +	{ +		(*errhandler)(libmail_gpgmime_getoutput(&gpg), errhandler_arg); +		return (-1); +	} + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(boundary, strlen(boundary), output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); + +	if (*iseof) +		return 0; + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(b->boundary, strlen(b->boundary), output_func_arg); +	if (clos_flag) +		(*output_func)("--", 2, output_func_arg); +	(*output_func)("\n", 1, output_func_arg); + +	if (clos_flag) +	{ +		libmail_mimestack_pop_to(stack, b); +		find_boundary(stack, iseof, input_func, +			      input_func_arg, output_func, +			      output_func_arg, 1); +	} + +	return 0; +} + +static int dogpgsign(const char *gpghome, const char *passphrase_fd, +		     struct mimestack **stack, struct header *h, int *iseof, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     void (*output_func)(const char *, +					 size_t, +					 void *), +		     void *output_func_arg, +		     int argc, +		     char **argv, +		     void (*errhandler)(const char *, void *), +		     void *errhandler_arg) +{ +	struct header *hp; +	char buf[8192]; +	struct gpgmime_forkinfo gpg; +	int clos_flag=0; +	struct mimestack *b=0; +	int rc=0; +	char signed_content_name[TEMPNAMEBUFSIZE]; +	int signed_content; +	FILE *signed_content_fp; +	const char *boundary; +	int need_crlf; +	struct gpg_fork_output_info gfoi; + +	for (hp=h; hp; hp=hp->next) +	{ +		if (encode_header(hp->header)) +			continue; +		(*output_func)(hp->header, strlen(hp->header), +			       output_func_arg); +	} + +	signed_content=libmail_tempfile(signed_content_name); +	if (signed_content < 0 || +	    (signed_content_fp=fdopen(signed_content, "w+")) == NULL) +	{ +		if (signed_content >= 0) +		{ +			close(signed_content); +			unlink(signed_content_name); +		} +		return -1; + +	} +	libmail_gpg_noexec(fileno(signed_content_fp)); +	unlink(signed_content_name);	/* UNIX semantics */ + +	for (hp=h; hp; hp=hp->next) +	{ +		const char *p; + +		if (!encode_header(hp->header)) +			continue; + +		for (p=hp->header; *p; p++) +		{ +			if (*p == '\r') +				continue; + +			if (*p == '\n') +				putc('\r', signed_content_fp); +			putc(*p, signed_content_fp); +		} +	} + +	/* +	** Chew the content until the next MIME boundary. +	*/ +	need_crlf=1; +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (need_crlf) +		{ +			if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL) +				break; + +			fprintf(signed_content_fp, "\r\n"); +		} + +		need_crlf=0; +		for (;;) +		{ +			for (p=buf; *p; p++) +			{ +				if (*p == '\r') +					continue; +				if (*p == '\n') +				{ +					need_crlf=1; +					break; +				} + +				putc(*p, signed_content_fp); +			} +			if (*p == '\n') +				break; + +			if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +			{ +				*iseof=1; +				break; +			} +		} +	} + +	/* +	** This needs some 'splainin.  Note that we spit out a newline at +	** the BEGINNING of each line, above.  This generates the blank +	** header->body separator line.  Now, if we're NOT doing multiline +	** content, we need to follow the last line of the content with a +	** newline.  If we're already doing multiline content, that extra +	** newline (if it exists) is already there. +	*/ + +	if (!*stack) +	{ +		fprintf(signed_content_fp, "\r\n"); +	} + +	if (fflush(signed_content_fp) < 0 || ferror(signed_content_fp)) +	{ +		fclose(signed_content_fp); +		return (-1); +	} + +	boundary=get_boundary(*stack, "", signed_content_fp); + +	if (my_rewind(signed_content_fp) < 0) +	{ +		fclose(signed_content_fp); +		return (-1); +	} + +#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg) +#define S(s) (*output_func)( s, strlen(s), output_func_arg) + +	C("Content-Type: multipart/signed;\n" +	  "    boundary=\""); +	S(boundary); +	C("\";\n" +	  "    micalg=pgp-sha1;" +	  " protocol=\"application/pgp-signature\"\n" +	  "\n" +	  "This is a MIME GnuPG-signed message.  If you see this text, it means that\n" +	  "your E-mail or Usenet software does not support MIME signed messages.\n" +	  "The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.\n" +	  "To open this message correctly you will need to install E-mail or Usenet\n" +	  "software that supports modern Internet standards.\n" +	  "\n--"); +	S(boundary); +	C("\n"); + + +	gfoi.output_func=output_func; +	gfoi.output_func_arg=output_func_arg; +	gfoi.gpgptr= &gpg; + +	if (libmail_gpgmime_forksignencrypt(gpghome, passphrase_fd, +					    GPG_SE_SIGN, +					    argc, argv, +					    &gpg_fork_output, &gfoi, +					    &gpg)) +	{ +		fclose(signed_content_fp); +		return (-1); +	} + +	while (fgets(buf, sizeof(buf), signed_content_fp) != NULL) +	{ +		char *p; +		size_t j, k; + +		libmail_gpgmime_write(&gpg, buf, strlen(buf)); + +		p=buf; +		for (j=k=0; p[j]; j++) +			if (p[j] != '\r') +				p[k++]=p[j]; + +		if (k) +			(*output_func)(p, k, output_func_arg); +	} + +	C("\n--"); +	S(boundary); + +	C("\n" +	  "Content-Type: application/pgp-signature\n" +	  "Content-Transfer-Encoding: 7bit\n\n"); + +#undef C +#undef S + +	if (libmail_gpgmime_finish(&gpg)) +		rc= -1; /* TODO */ + +	if (rc) +	{ +		(*errhandler)(libmail_gpgmime_getoutput(&gpg), +			      errhandler_arg); +		fclose(signed_content_fp); +		return -1; +	} + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(boundary, strlen(boundary), output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); + +	fclose(signed_content_fp); +	if (*iseof) +		return 0; + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(b->boundary, strlen(b->boundary), output_func_arg); +	if (clos_flag) +		(*output_func)("--", 2, output_func_arg); +	(*output_func)("\n", 1, output_func_arg); + +	if (clos_flag) +	{ +		libmail_mimestack_pop_to(stack, b); +		find_boundary(stack, iseof, input_func, input_func_arg, +			      output_func, output_func_arg, 1); +	} +	return 0; +} + +static int isgpg(struct mime_header *); +static int checksign(const char *gpghome, +		     const char *passphrase_fd, +		     struct mimestack **, int *, struct header *, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     void (*)(const char *, size_t, void *), +		     void *, +		     int, char **, +		     int *); +static int decrypt(const char *gpghome, +		   const char *passphrase_fd, +		   struct mimestack **, int *, struct header *, +		   int (*input_func)(char *, size_t, void *vp), +		   void *input_func_arg, +		   void (*)(const char *, size_t, void *), +		   void *, +		   int, char **, +		   int *); + +static void print_noncontent_headers(struct header *h, +				     void (*output_func)(const char *, +							 size_t, +							 void *), +				     void *output_func_arg) +{ +	struct header *p; + +	for (p=h; p; p=p->next) +	{ +		if (strncasecmp(p->header, "content-", 8) == 0) +			continue; +		(*output_func)(p->header, strlen(p->header), output_func_arg); +	} +} + +static int dosignencode2(int dosign, int doencode, int dodecode, +			 const char *gpghome, +			 const char *passphrase_fd, +			 int (*input_func)(char *, size_t, void *vp), +			 void *input_func_arg, +			 void (*output_func)(const char *, +					     size_t, +					     void *), +			 void *output_func_arg, +			 void (*errhandler_func)(const char *, void *), +			 void *errhandler_arg, +			 int argc, char **argv, +			 int *status) +{ +	struct mimestack *boundary_stack=0; +	int iseof=0; + +	*status=0; + +	while (!iseof) +	{ +		int errflag; + +		static const char ct_s[]="content-type:"; +		struct header *h=read_headers(&boundary_stack, &iseof, +					      input_func, input_func_arg, +					      output_func, +					      output_func_arg, +					      dodecode ? 0:1, +					      &errflag), +			*hct; + +		if (errflag) +			return 1; + +		if (iseof && !h) +			continue;	/* Artifact */ + +		hct=libmail_header_find(h, ct_s); + +		/* +		** If this is a multipart MIME section, we can keep on +		** truckin'. +		** +		*/ + +		if (hct) +		{ +			struct mime_header *mh= +				libmail_mimeheader_parse(hct->header+ +							 (sizeof(ct_s)-1)); +			const char *bv; + +			if (!mh) +			{ +				libmail_header_free(h); +				return (-1); +			} + +			if (strcasecmp(mh->header_name, "multipart/x-mimegpg") +			    == 0) +			{ +				/* Punt */ + +				char *buf=malloc(strlen(hct->header)+100); +				const char *p; + +				if (!buf) +				{ +					libmail_mimeheader_free(mh); +					libmail_header_free(h); +					return (-1); +				} +				strcpy(buf, "Content-Type: multipart/mixed"); +				p=strchr(hct->header, ';'); +				strcat(buf, p ? p:""); +				free(hct->header); +				hct->header=buf; + +				libmail_mimeheader_free(mh); +				mh=libmail_mimeheader_parse(hct->header+ +							    sizeof(ct_s)-1); +				if (!mh) +				{ +					libmail_header_free(h); +					return (-1); +				} +			} + +			if (strncasecmp(mh->header_name, "multipart/", 10)==0 +			    && (bv=libmail_mimeheader_getattr(mh, "boundary")) != 0 + +			    && (doencode & LIBMAIL_GPG_ENCAPSULATE) == 0 + +			    && !dosign +			    ) +			{ +				struct header *p; + +				if (libmail_mimestack_push(&boundary_stack, +							   bv) < 0) +				{ +					libmail_header_free(h); +					return (-1); +				} + +				if (dodecode) +				{ +					int rc; + +					if (strcasecmp(mh->header_name, +						       "multipart/signed")==0 +					    && (dodecode & LIBMAIL_GPG_CHECKSIGN) +					    && isgpg(mh)) +					{ +						int errflag; + +						print_noncontent_headers(h, +									 output_func, +									 output_func_arg +									 ); +						libmail_mimeheader_free(mh); +						rc=checksign(gpghome, +							     passphrase_fd, +							     &boundary_stack, +							     &iseof, +							     h, +							     input_func, +							     input_func_arg, +							     output_func, +							     output_func_arg, +							     argc, argv, +							     &errflag); +						libmail_header_free(h); + +						if (errflag) +							*status |= +								LIBMAIL_ERR_VERIFYSIG; +						if (rc) +							return -1; + +						continue; +					} + +					if (strcasecmp(mh->header_name, +						       "multipart/encrypted") +					    ==0 +					    && (dodecode & LIBMAIL_GPG_UNENCRYPT) +					    && isgpg(mh)) +					{ +						int errflag; + +						print_noncontent_headers(h, +									 output_func, +									 output_func_arg +									 ); +						libmail_mimeheader_free(mh); +						rc=decrypt(gpghome, +							   passphrase_fd, +							   &boundary_stack, +							   &iseof, +							   h, +							   input_func, +							   input_func_arg, +							   output_func, +							   output_func_arg, +							   argc, argv, +							   &errflag); +						libmail_header_free(h); + +						if (errflag) +							*status |= +								LIBMAIL_ERR_DECRYPT; +						if (rc) +							return -1; +						continue; +					} +				} + +				for (p=h; p; p=p->next) +				{ +					(*output_func)(p->header, +						       strlen(p->header), +						       output_func_arg); +				} + +				(*output_func)("\n", 1, output_func_arg); +				libmail_header_free(h); +				libmail_mimeheader_free(mh); + +				find_boundary(&boundary_stack, &iseof, +					      input_func, +					      input_func_arg, +					      output_func, +					      output_func_arg, dodecode ? 0:1); +				continue; +			} +			libmail_mimeheader_free(mh); +		} + +		if (dodecode) +		{ +			struct header *p; +			int is_message_rfc822=0; + +			for (p=h; p; p=p->next) +			{ +				(*output_func)(p->header, +					       strlen(p->header), +					       output_func_arg); +			} +			(*output_func)("\n", 1, output_func_arg); + +			/* +			** If this is a message/rfc822 attachment, we can +			** resume reading the next set of headers. +			*/ + +			hct=libmail_header_find(h, ct_s); +			if (hct) +			{ +				struct mime_header *mh= +					libmail_mimeheader_parse(hct->header+ +								 (sizeof(ct_s) +								  -1)); +				if (!mh) +				{ +					libmail_header_free(h); +					return (-1); +				} + +				if (strcasecmp(mh->header_name, +					       "message/rfc822") == 0) +					is_message_rfc822=1; +				libmail_mimeheader_free(mh); +			} +			libmail_header_free(h); + +			if (!is_message_rfc822) +				find_boundary(&boundary_stack, &iseof, +					      input_func, +					      input_func_arg, +					      output_func, +					      output_func_arg, 0); +			continue; +		} + +		if (doencode ? +		    dogpgencrypt(gpghome, +				 passphrase_fd, +				 &boundary_stack, h, &iseof, +				 input_func, +				 input_func_arg, +				 output_func, +				 output_func_arg, +				 argc, argv, dosign, +				 errhandler_func, errhandler_arg) +		    : +		    dogpgsign(gpghome, +			      passphrase_fd, +			      &boundary_stack, h, &iseof, +			      input_func, +			      input_func_arg, +			      output_func, +			      output_func_arg, +			      argc, argv, +			      errhandler_func, errhandler_arg)) +		{ +			libmail_header_free(h); +			return 1; +		} + +		libmail_header_free(h); +	} + +	return (0); +} + + +static int isgpg(struct mime_header *mh) +{ +	const char *attr; + +	attr=libmail_mimeheader_getattr(mh, "protocol"); + +	if (!attr) +		return (0); + +	if (strcasecmp(attr, "application/pgp-encrypted") == 0) +		return (1); + +	if (strcasecmp(attr, "application/pgp-signature") == 0) +	{ +		return (1); +	} +	return (0); +} + +static int nybble(char c) +{ +	static const char x[]="0123456789ABCDEFabcdef"; + +	const char *p=strchr(x, c); +	int n; + +	if (!p) p=x; + +	n= p-x; +	if (n >= 16) +		n -= 6; +	return (n); +} + +/* +** Check signature +*/ + +static int dochecksign(const char *, const char *, +		       struct mimestack *, +		       FILE *, +		       void (*output_func)(const char *, +					   size_t, +					   void *), +		       void *output_func_arg, +		       const char *, +		       const char *, +		       int, char **, int *); + +static int checksign(const char *gpghome, +		     const char *passphrase_fd, +		     struct mimestack **stack, int *iseof, +		     struct header *h, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     void (*output_func)(const char *, +					 size_t, +					 void *), +		     void *output_func_arg, +		     int argc, char **argv, int *errptr) +{ +	char buf[BUFSIZ]; +	struct header *h2; + +	char signed_content[TEMPNAMEBUFSIZE]; +	char signature[TEMPNAMEBUFSIZE]; +	int signed_file, signature_file; +	FILE *signed_file_fp, *signature_file_fp; +	int clos_flag; +	int need_nl, check_boundary; +	struct mimestack *b=0; +	struct mime_header *mh; +	int qpdecode=0; +	int errflag; + +	*errptr=0; + +	signed_file=libmail_tempfile(signed_content); + +	if (signed_file < 0 || (signed_file_fp=fdopen(signed_file, "w+")) == 0) +	{ +		if (signed_file > 0) +		{ +			close(signed_file); +			unlink(signed_content); +		} +		return -1; +	} +	libmail_gpg_noexec(fileno(signed_file_fp)); + +	find_boundary(stack, iseof, input_func, +		      input_func_arg, NULL, NULL, 0); +	if (*iseof) +		return 0; + +	need_nl=0; +	check_boundary=1; + +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			continue; +		} + +		if (check_boundary +		    && (b=is_boundary(*stack, buf, &clos_flag)) != 0) +			break; +		if (need_nl) +			fprintf(signed_file_fp, "\r\n"); + +		for (p=buf; *p && *p != '\n'; p++) +			putc(*p, signed_file_fp); +		need_nl=check_boundary= *p != 0; +	} + +	if (my_rewind(signed_file_fp) < 0) +	{ +		fclose(signed_file_fp); +		unlink(signed_content); +		return -1; +	} + +	if (clos_flag) +	{ +		fclose(signed_file_fp); +		unlink(signed_content); +		if (b) +			libmail_mimestack_pop_to(stack, b); +		find_boundary(stack, iseof, input_func, input_func_arg, +			      output_func, output_func_arg, 1); +		return 0; +	} + +	h=read_headers(stack, iseof, input_func, input_func_arg, +		       output_func, output_func_arg, 0, &errflag); + +	if (errflag) +	{ +		fclose(signed_file_fp); +		unlink(signed_content); + +		return (-1); +	} + +	if (!h || !(h2=libmail_header_find(h, "content-type:"))) +	{ +		if (h) +			libmail_header_free(h); +		fclose(signed_file_fp); +		unlink(signed_content); +		if (!*iseof) +			find_boundary(stack, iseof, input_func, input_func_arg, +				      output_func, output_func_arg, 1); +		return 0; +	} + +	mh=libmail_mimeheader_parse(h2->header+sizeof("content-type:")-1); + +	if (!mh) +	{ +		libmail_header_free(h); +		fclose(signed_file_fp); +		unlink(signed_content); +		return (-1); +	} + +	if (strcasecmp(mh->header_name, "application/pgp-signature")) +	{ +		libmail_mimeheader_free(mh); +		libmail_header_free(h); +		fclose(signed_file_fp); +		unlink(signed_content); +		if (!*iseof) +			find_boundary(stack, iseof, input_func, input_func_arg, +				      output_func, output_func_arg, 1); +		return (0); +	} +	libmail_mimeheader_free(mh); + +	/* +	** In rare instances, the signature is qp-encoded. +	*/ + +	if ((h2=libmail_header_find(h, "content-transfer-encoding:")) != NULL) +	{ +		mh=libmail_mimeheader_parse +			(h2->header+sizeof("content-transfer-encoding:")-1); + +		if (!mh) +		{ +			libmail_header_free(h); +			fclose(signed_file_fp); +			unlink(signed_content); +			return -1; +		} + +		if (strcasecmp(mh->header_name, +			       "quoted-printable") == 0) +			qpdecode=1; +		libmail_mimeheader_free(mh); +	} +	libmail_header_free(h); + +	signature_file=libmail_tempfile(signature); + +	if (signature_file < 0 +	    || (signature_file_fp=fdopen(signature_file, "w+")) == 0) +	{ +		if (signature_file > 0) +		{ +			close(signature_file); +			unlink(signature); +		} +		unlink(signed_content); +		return (-1); +	} + +	while (!*iseof) +	{ +		const char *p; + +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			continue; +		} + +		if ((b=is_boundary(*stack, buf, &clos_flag)) != 0) +			break; + +		for (p=buf; *p; p++) +		{ +			int n; + +			if (!qpdecode) +			{ +				putc(*p, signature_file_fp); +				continue; +			} + +			if (*p == '=' && p[1] == '\n') +				break; + +			if (*p == '=' && p[1] && p[2]) +			{ +				n=nybble(p[1]) * 16 + nybble(p[2]); +				if ( (char)n ) +				{ +					putc((char)n, signature_file_fp); +					p += 2; +				} +				p += 2; +				continue; +			} +			putc(*p, signature_file_fp); + +			/* If some spits out qp-lines > BUFSIZ, they deserve +			** this crap. +			*/ +		} +	} + +	fflush(signature_file_fp); +	if (ferror(signature_file_fp)) +	{ +		unlink(signature); +		fclose(signed_file_fp); +		unlink(signed_content); +		return -1; +	} +	if (fclose(signature_file_fp)) +	{ +		unlink(signature); +		unlink(signed_content); +		return -1; +	} + +	errflag=dochecksign(gpghome, +			    passphrase_fd, +			    *stack, signed_file_fp, +			    output_func, output_func_arg, signed_content, +			    signature, +			    argc, argv, errptr); + +	fclose(signed_file_fp); +	unlink(signature); +	unlink(signed_content); + +	if (errflag) +		return -1; + +	while (!clos_flag) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} +		if (!(b=is_boundary(*stack, buf, &clos_flag))) +			clos_flag=0; +	} +	if (b) +		libmail_mimestack_pop_to(stack, b); + +	return 0; +} + +static const char *newboundary() +{ +	static char buffer[256]; +	static unsigned counter=0; +	time_t t; +	char hostnamebuf[256]; + +	time(&t); +	hostnamebuf[sizeof(hostnamebuf)-1]=0; +	if (gethostname(hostnamebuf, sizeof(hostnamebuf)-1) < 0) +		hostnamebuf[0]=0; + +	sprintf(buffer, "=_%-1.30s-%u-%u-%04u", +		hostnamebuf, (unsigned)getpid(), +		(unsigned)t, ++counter); +	return (buffer); +} + +static int good_boundary(const char *boundary, +			 struct mimestack *m, const char *errmsg, FILE *fp) +{ +	int dummy; +	int l=strlen(boundary); +	const char *p; +	char buf[BUFSIZ]; + +	if (is_boundary(m, boundary, &dummy)) +		return (0); + +	for (p=errmsg; *p; ) +	{ +		if (*p == '-' && p[1] == '-' && strncasecmp(p+2, boundary, l) +		    == 0) +			return (0); + +		while (*p) +			if (*p++ == '\n') +				break; +	} + +	if (fp) +	{ +		if (my_rewind(fp) < 0) +			return 0; + +		while (fgets(buf, sizeof(buf), fp)) +		{ +			if (buf[0] == '-' && buf[1] == '-' && +			    strncasecmp(buf+2, boundary, l) == 0) +				return (0); +		} +	} +	return (1); +} + +static const char *get_boundary(struct mimestack *m, +				const char *errmsg, +				FILE *fp) +{ +	const char *p; + +	do +	{ +		p=newboundary(); +	} while (!good_boundary(p, m, errmsg, fp)); +	return (p); +} + +static const char *encoding_str(const char *p) +{ +	while (*p) +	{ +		if (*p <= 0 || *p >= 0x7F) +			return ("8bit"); +		++p; +	} +	return ("7bit"); +} + + +static int copyfp(FILE *t, +		  void (*output_func)(const char *, +				      size_t, +				      void *), +		  void *output_func_arg, +		  int stripcr) +{ +	char buf[BUFSIZ]; +	int rc=0; + +	while ((rc=fread(buf, 1, sizeof(buf), t)) > 0) +	{ +		if (stripcr) +		{ +			int i, j; + +			for (i=j=0; i<rc; ++i) +			{ +				if (buf[i] != '\r') +					buf[j++]=buf[i]; +			} +			rc=j; +		} +		(*output_func)(buf, rc, output_func_arg); +	} + +	return rc; +} + +static void open_result_multipart(void (*)(const char *, size_t, void *), +				  void *, +				  int, const char *, const char *, +				  const char *); + +static int dochecksign(const char *gpghome, +		       const char *passphrase_fd, +		       struct mimestack *stack, +		       FILE *content_fp, +		       void (*output_func)(const char *, +					   size_t, +					   void *), +		       void *output_func_arg, +		       const char *content_filename, +		       const char *signature_filename, +		       int argc, +		       char **argv, int *errptr) +{ +	struct gpgmime_forkinfo gpg; +	int i; +	const char *new_boundary; +	const char *output; + +	if (libmail_gpgmime_forkchecksign(gpghome, passphrase_fd, +				  content_filename, +				  signature_filename, +				  argc, argv, +				  &gpg)) +	{ +		return -1; +	} + +	*errptr=i=libmail_gpgmime_finish(&gpg); + +	output=libmail_gpgmime_getoutput(&gpg); + +	new_boundary=get_boundary(stack, output, content_fp); + +	open_result_multipart(output_func, output_func_arg, +			      i, new_boundary, output, +			      libmail_gpgmime_getcharset(&gpg)); + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(new_boundary, strlen(new_boundary), output_func_arg); +	(*output_func)("\n", 1, output_func_arg); + +	if (my_rewind(content_fp) < 0) +	{ +		return -1; +	} + +	if (copyfp(content_fp, output_func, output_func_arg, 1)) +		return -1; + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(new_boundary, strlen(new_boundary), output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); +	return 0; +} + +static void open_result_multipart(void (*output_func)(const char *, +						      size_t, +						      void *), +				  void *output_func_arg, +				  int rc, +				  const char *new_boundary, +				  const char *err_str, +				  const char *err_charset) +{ +#define C(s) (*output_func)( s, sizeof(s)-1, output_func_arg) +#define S(s) (*output_func)( s, strlen(s), output_func_arg) + +	char n[10]; +	const char *p; + +	sprintf(n, "%d", rc); + +	C("Content-Type: multipart/x-mimegpg; xpgpstatus="); + +	S(n); + +	C("; boundary=\""); + +	S(new_boundary); + +	C("\"\n" +	  "\nThis is a MIME GnuPG-processed message.  If you see this text, it means\n" +	  "that your E-mail or Usenetsoftware does not support MIME-formatted messages.\n\n" +	  "--"); + +	S(new_boundary); +	C("\nContent-Type: text/x-gpg-output; charset="); +	S(err_charset); +	C("\nContent-Transfer-Encoding: "); + +	p=encoding_str(err_str); +	S(p); +	C("\n\n"); +	S(err_str); +#undef C +#undef S +} + +static void close_mime(struct mimestack **stack, int *iseof, +		       int (*input_func)(char *, size_t, void *vp), +		       void *input_func_arg, +		       void (*output_func)(const char *, +					   size_t, +					   void *), +		       void *output_func_arg) +{ +	char buf[BUFSIZ]; +	int is_closing; +	struct mimestack *b; + +	for (;;) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		(*output_func)(buf, strlen(buf), output_func_arg); +		if (!(b=is_boundary(*stack, buf, &is_closing))) +			continue; +		if (!is_closing) +			continue; + +		libmail_mimestack_pop_to(stack, b); +		break; +	} +} + +static int dodecrypt(const char *, const char *, +		     struct mimestack **, int *, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     FILE *, int, char **, const char *, +		     void (*)(const char *, +			      size_t, +			      void *), +		     void *, +		     int *); + +static void write_temp_fp(const char *p, size_t n, void *vp) +{ +	if (fwrite(p, n, 1, (FILE *)vp) != 1) +		; /* ignored */ +} + +static int decrypt(const char *gpghome, +		   const char *passphrase_fd, +		   struct mimestack **stack, int *iseof, +		   struct header *h, +		   int (*input_func)(char *, size_t, void *vp), +		   void *input_func_arg, +		   void (*output_func)(const char *, +				       size_t, +				       void *), +		   void *output_func_arg, +		   int argc, char **argv, +		   int *errptr) +{ +	struct header *p, *q; +	char temp_file[TEMPNAMEBUFSIZE]; +	int temp_fd; +	FILE *temp_fp; +	struct mime_header *mh; +	int flag; +	int errflag; + +	*errptr=0; +	temp_fd=libmail_tempfile(temp_file); +	if (temp_fd < 0 || (temp_fp=fdopen(temp_fd, "w+")) == 0) +	{ +		if (temp_fd >= 0) +			close(temp_fd); +		return -1; +	} + +	for (p=h; p; p=p->next) +	{ +		fprintf(temp_fp, "%s", p->header); +	} +	putc('\n', temp_fp); + +	find_boundary(stack, iseof, input_func, input_func_arg, +		      write_temp_fp, temp_fp, 0); +	if (*iseof) +	{ +		fclose(temp_fp); +		unlink(temp_file); +		return (0); +	} + +	p=read_headers(stack, iseof, input_func, input_func_arg, write_temp_fp, +		       temp_fp, 0, &errflag); + +	if (*iseof || errflag) +	{ +		libmail_header_free(p); +		fclose(temp_fp); +		unlink(temp_file); + +		if (errflag) +			return -1; +		return 0; +	} + +	q=libmail_header_find(p, "content-type:"); + +	flag=0; + +	if (q) +	{ +		mh=libmail_mimeheader_parse(q->header+13); +		if (!mh) +		{ +			libmail_header_free(p); +			fclose(temp_fp); +			unlink(temp_file); +			return -1; +		} + +		if (strcasecmp(mh->header_name, "application/pgp-encrypted") +		    == 0) +			flag=1; +		libmail_mimeheader_free(mh); +	} + +	for (q=p; q; q=q->next) +	{ +		fprintf(temp_fp, "%s", q->header); +	} +	libmail_header_free(p); +	putc('\n', temp_fp); + +	p=read_headers(stack, iseof, input_func, input_func_arg, +		       write_temp_fp, temp_fp, 0, +		       &errflag); + +	if (*iseof || errflag) +	{ +		libmail_header_free(p); +		fclose(temp_fp); +		unlink(temp_file); + +		if (errflag) +			return -1; + +		return 0; +	} + +	q=libmail_header_find(p, "version:"); + +	if (flag) +	{ +		if (!q || atoi(p->header + 8) != 1) +			flag=0; +	} +	for (q=p; q; q=q->next) +	{ +		fprintf(temp_fp, "%s", q->header); +	} +	libmail_header_free(p); +	putc('\n', temp_fp); + +	find_boundary(stack, iseof, input_func, input_func_arg, +		      write_temp_fp, temp_fp, 0); + +	if (*iseof) +	{ +		fclose(temp_fp); +		unlink(temp_file); +		return 0; +	} + +	p=read_headers(stack, iseof, input_func, input_func_arg, write_temp_fp, +		       temp_fp, 0, &errflag); + +	if (*iseof || errflag) +	{ +		libmail_header_free(p); +		fclose(temp_fp); +		unlink(temp_file); + +		if (errflag) +			return -1; + +		return 0; +	} + +	q=libmail_header_find(p, "content-type:"); + +	if (q && flag) +	{ +		flag=0; +		mh=libmail_mimeheader_parse(q->header+13); +		if (!mh) +		{ +			libmail_header_free(p); +			fclose(temp_fp); +			unlink(temp_file); +			return -1; +		} + +		if (strcasecmp(mh->header_name, "application/octet-stream") +		    == 0) +			flag=1; +		libmail_mimeheader_free(mh); + +		q=libmail_header_find(p, "content-transfer-encoding:"); +		if (q && flag) +		{ +			flag=0; +			mh=libmail_mimeheader_parse(strchr(q->header, ':')+1); +			if (!mh) +			{ +				libmail_header_free(p); +				fclose(temp_fp); +				unlink(temp_file); +				return -1; +			} + +			if (strcasecmp(mh->header_name, "7bit") == 0 || +			    strcasecmp(mh->header_name, "8bit") == 0) +				flag=1; +			libmail_mimeheader_free(mh); +		} +	} + +	for (q=p; q; q=q->next) +	{ +		fprintf(temp_fp, "%s", q->header); +	} +	libmail_header_free(p); +	putc('\n', temp_fp); + +	if (fflush(temp_fp) || ferror(temp_fp) || my_rewind(temp_fp) < 0) +	{ +		fclose(temp_fp); +		unlink(temp_file); +		return -1; +	} + +	if (!flag) +	{ +		int c=copyfp(temp_fp, output_func, output_func_arg, 0); + +		fclose(temp_fp); +		unlink(temp_file); +		close_mime(stack, iseof, input_func, input_func_arg, +			   output_func, output_func_arg); +		return (c); +	} + +	fclose(temp_fp); +	if ((temp_fp=fopen(temp_file, "w+")) == NULL) +	{ +		unlink(temp_file); +		return (-1); +	} +	libmail_gpg_noexec(fileno(temp_fp)); +	errflag=dodecrypt(gpghome, passphrase_fd, +			  stack, iseof, input_func, input_func_arg, +			  temp_fp, argc, argv, temp_file, +			  output_func, output_func_arg, errptr); +	fclose(temp_fp); +	unlink(temp_file); +	return errflag; +} + +static int dumpdecrypt(const char *c, size_t n, void *vp) +{ +	FILE *fp=(FILE *)vp; + +	if (n == 0) +		return 0; + +	if (fwrite(c, n, 1, fp) != 1) +		return -1; +	return (0); +} + +static int dodecrypt(const char *gpghome, +		     const char *passphrase_fd, +		     struct mimestack **stack, int *iseof, +		     int (*input_func)(char *, size_t, void *vp), +		     void *input_func_arg, +		     FILE *fpout, int argc, char **argv, +		     const char *temp_file, +		     void (*output_func)(const char *, +			      size_t, +			      void *), +		     void *output_func_arg, +		     int *errptr) +{ +	struct gpgmime_forkinfo gpg; +	char buf[BUFSIZ]; +	int is_closing; +	struct mimestack *b=NULL; +	int dowrite=1; +	int rc; +	const char *new_boundary; +	const char *output; + +	if (libmail_gpgmime_forkdecrypt(gpghome, +				passphrase_fd, +				argc, argv, &dumpdecrypt, fpout, &gpg)) +		return -1; + +	for (;;) +	{ +		if ( (*input_func)(buf, sizeof(buf), input_func_arg)) +		{ +			*iseof=1; +			break; +		} + +		if (dowrite) +			libmail_gpgmime_write(&gpg, buf, strlen(buf)); + +		if (!(b=is_boundary(*stack, buf, &is_closing))) +			continue; +		dowrite=0; +		if (!is_closing) +			continue; +		break; +	} + +	rc=libmail_gpgmime_finish(&gpg); +	if (fflush(fpout) || ferror(fpout) || my_rewind(fpout) < 0) +	{ +		fclose(fpout); +		unlink(temp_file); +		return -1; +	} + +	if (*iseof) +		return 0; + +	output=libmail_gpgmime_getoutput(&gpg), + +	new_boundary=get_boundary(*stack, output, rc ? NULL:fpout); + +	open_result_multipart(output_func, +			      output_func_arg, rc, new_boundary, +			      output, +			      libmail_gpgmime_getcharset(&gpg)); + +	*errptr=rc; + +#if 0 + +	/* +	** gnupg returns non-zero exit even if succesfully unencrypted, when +	** just the signature is bad. +	*/ +	if (rc == 0) +#endif +	{ +		if (fseek(fpout, 0L, SEEK_SET) < 0) +		{ +			fclose(fpout); +			unlink(temp_file); +			return -1; +		} + +		(*output_func)("\n--", 3, output_func_arg); +		(*output_func)(new_boundary, strlen(new_boundary), +			       output_func_arg); +		(*output_func)("\n", 1, output_func_arg); + +		if (copyfp(fpout, output_func, output_func_arg, 1)) +		    return -1; +	} + +	(*output_func)("\n--", 3, output_func_arg); +	(*output_func)(new_boundary, strlen(new_boundary), +		       output_func_arg); +	(*output_func)("--\n", 3, output_func_arg); + +	libmail_mimestack_pop_to(stack, b); +	return 0; +} + +struct libmail_gpg_errhandler { + +	struct libmail_gpg_info *options; +	int err_flag; +}; + +static void libmail_gpg_errfunc(const char *errmsg, void *vp) +{ +	struct libmail_gpg_errhandler *eh=(struct libmail_gpg_errhandler *)vp; + +	if (!eh->err_flag) +	{ +		eh->err_flag=1; + +		(*eh->options->errhandler_func)(errmsg, +						eh->options->errhandler_arg); +	} +} +  +static int input_func_from_fp(char *buf, size_t cnt, void *vp) +{ +	if (fgets(buf, cnt, (FILE *)vp) == NULL) +		return (-1); +	return (0); +} + +/* +** When signing, but not encoding, signed text must be 7bit, as per RFC. +** +** Use rfc2045's rewriter to do this. +*/ + +static int dosignencode(int dosign, int doencode, int dodecode, +			const char *gpghome, +			const char *passphrase_fd, +			int (*input_func)(char *, size_t, void *vp), +			void *input_func_arg, +			void (*output_func)(const char *, +					    size_t, +					    void *), +			void *output_func_arg, +			void (*errhandler_func)(const char *, void *), +			void *errhandler_arg, +			int argc, char **argv, +			int *status) +{ +	char temp_decode_name[TEMPNAMEBUFSIZE]; +	int fdin; +	int fdout; +	FILE *fdin_fp; +	char buffer[8192]; +	struct rfc2045src *src; +	struct rfc2045 *rfcp; +	int rc; + +	if (!dosign || doencode) +		return dosignencode2(dosign, doencode, dodecode, +				     gpghome, +				     passphrase_fd, +				     input_func, +				     input_func_arg, +				     output_func, +				     output_func_arg, +				     errhandler_func, +				     errhandler_arg, +				     argc, argv, status); + +	/* Save the message into a temp file, first */ + +	fdin=libmail_tempfile(temp_decode_name); + +	if (fdin < 0 || +	    (fdin_fp=fdopen(fdin, "w+")) == NULL) +	{ +		if (fdin >= 0) +			close(fdin); + +		(*errhandler_func)("Cannot create temporary file", +				   errhandler_arg); +		return (-1); +	} + +	unlink(temp_decode_name); + +	if (!(rfcp=rfc2045_alloc_ac())) +	{ +		(*errhandler_func)(strerror(errno), errhandler_arg); +		fclose(fdin_fp); +		return (-1); +	} + +	while ( (*input_func)(buffer, sizeof(buffer), input_func_arg) == 0) +	{ +		size_t l=strlen(buffer); + +		if (fwrite(buffer, l, 1, fdin_fp) != 1) +		{ +			(*errhandler_func)(strerror(errno), errhandler_arg); +			fclose(fdin_fp); +			rfc2045_free(rfcp); +			return (-1); +		} + +		/* Parse the message at the same time it's being saved */ + +		rfc2045_parse(rfcp, buffer, l); +	} + +	if (fseek(fdin_fp, 0L, SEEK_SET) < 0) +	{ +		(*errhandler_func)(strerror(errno), errhandler_arg); +		fclose(fdin_fp); +		rfc2045_free(rfcp); +		return (-1); +	} + +	if (!rfc2045_ac_check(rfcp, RFC2045_RW_7BIT)) +	{ +		rfc2045_free(rfcp); + +		/* No need to rewrite, just do this */ + +		rc=dosignencode2(dosign, doencode, dodecode, +				     gpghome, +				     passphrase_fd, +				     input_func_from_fp, +				     fdin_fp, +				     output_func, +				     output_func_arg, +				     errhandler_func, +				     errhandler_arg, +				     argc, argv, status); + +		fclose(fdin_fp); + +		return rc; +	} + +	/* Rewrite the message into another temp file */ + +	fdout=libmail_tempfile(temp_decode_name); + +	src=rfc2045src_init_fd(fileno(fdin_fp)); + +	if (fdout < 0 || src == NULL || +	    rfc2045_rewrite(rfcp, src, fdout, "mimegpg") < 0 || +	    lseek(fdout, 0L, SEEK_SET) < 0) +	{ +		if (fdout >= 0) +			close(fdout); +		if (src) +			rfc2045src_deinit(src); + +		(*errhandler_func)(strerror(errno), errhandler_arg); +		rfc2045_free(rfcp); +		fclose(fdin_fp); +		return (-1); +	} +	fclose(fdin_fp); +	rfc2045_free(rfcp); +	rfc2045src_deinit(src); + +	/* Now, read the converted message, from the temp file */ + +	if ((fdin_fp=fdopen(fdout, "w+")) == NULL) +	{ +		close(fdout); + +		(*errhandler_func)("Cannot create temporary file", +				   errhandler_arg); +		return (-1); +	} + +	rc=dosignencode2(dosign, doencode, dodecode, +			 gpghome, +			 passphrase_fd, +			 input_func_from_fp, +			 fdin_fp, +			 output_func, +			 output_func_arg, +			 errhandler_func, +			 errhandler_arg, +			 argc, argv, status); +	fclose(fdin_fp); +	return rc; +} + +int libmail_gpg_signencode(int dosign, +			   int doencode, +			   /* +			   ** One of LIBMAIL_GPG_INDIVIDUAL or +			   ** LIBMAIL_GPG_ENCAPSULATE +			   */ +			   struct libmail_gpg_info *options) +{ +	int rc; +	struct libmail_gpg_errhandler eh; + +	eh.options=options; +	eh.err_flag=0; + +	if (doencode != LIBMAIL_GPG_INDIVIDUAL && +	    doencode != LIBMAIL_GPG_ENCAPSULATE) +		doencode=0; + +	if (!dosign && !doencode) +	{ +		(*options->errhandler_func)("Invalid arguments to" +					    " libmail_gpg_signencode", +					    options->errhandler_arg); +		return -1; +	} + +	rc=dosignencode(dosign, doencode, 0, +			options->gnupghome, +			options->passphrase_fd, +			options->input_func, +			options->input_func_arg, +			options->output_func, +			options->output_func_arg, +			&libmail_gpg_errfunc, +			&eh, +			options->argc, +			options->argv, +			&options->errstatus); + +	if (rc && !eh.err_flag) +		(*options->errhandler_func)(strerror(errno), +					    options->errhandler_arg); +	return rc; +} + +int libmail_gpg_decode(int mode, +		       /* +		       ** LIBMAIL_GPG_UNENCRYPT OR LIBMAIL_GPG_CHECKSIGN +		       */ +		       struct libmail_gpg_info *options) +{ +	int rc; +	struct libmail_gpg_errhandler eh; + +	eh.options=options; +	eh.err_flag=0; + +	if ((mode & (LIBMAIL_GPG_UNENCRYPT|LIBMAIL_GPG_CHECKSIGN)) == 0) +	{ +		(*options->errhandler_func)("Invalid arguments to" +					    " libmail_gpg_decode", +					    options->errhandler_arg); +		return -1; +	} + +	rc=dosignencode(0, 0, mode, +			options->gnupghome, +			options->passphrase_fd, +			options->input_func, +			options->input_func_arg, +			options->output_func, +			options->output_func_arg, +			&libmail_gpg_errfunc, +			&eh, +			options->argc, +			options->argv, +			&options->errstatus); + +	if (rc && !eh.err_flag) +		(*options->errhandler_func)(strerror(errno), +					    options->errhandler_arg); +	return rc; +} diff --git a/gpglib/gpglib.h b/gpglib/gpglib.h new file mode 100644 index 0000000..784f9b8 --- /dev/null +++ b/gpglib/gpglib.h @@ -0,0 +1,239 @@ +#ifndef	gpglib_h +#define	gpglib_h +/* +** Copyright 2001-2008 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#ifdef  __cplusplus +extern "C" { +#endif + +#include	"config.h" +#include	<sys/types.h> +#include	<stdlib.h> + + +#define LIBMAIL_GPG_INDIVIDUAL	1 +#define LIBMAIL_GPG_ENCAPSULATE	2 + +#define LIBMAIL_GPG_CHECKSIGN	1 +#define LIBMAIL_GPG_UNENCRYPT	2 + +struct libmail_gpg_info { + +	const char *gnupghome; /* May be NULL, sets GNUPGHOME */ + +	const char *passphrase_fd; /* NULL, or string giving */ + +	/* +	** input_func gets called repeatedly to obtain the message to +	** encrypt/sign/decrypt/check.  input_func() receives the same +	** arguments as fgets(), with its third argument being input_func_arg. +	** input_func should read up to cnt-1 bytes, or a newline, whichever +	** comes first, and save read data in buf, appending a single null +	** byte.  input_func should return 0, or -1 on EOF condition. +	*/ +	int (*input_func)(char *buf, size_t cnt, void *vp); +	void *input_func_arg; + +	/* +	** Output_func gets repeatedly invoked with the contents of the +	** encrypted/signed/decrypted/verified message. +	*/ + +	void (*output_func)(const char *output, size_t nbytes, +			    void *output_arg); +	void *output_func_arg; /* Passthru arg to output_func */ + +	/* +	** In the event of an error, the error handler will be invoked with +	** the error message text.  The error handler will be invoked +	** just before libmail_gpg_*() exits.  Note that the memory used +	** by the error message text will be destroyed by the time +	** libmail_gpg_*() exits, so the application needs to make a copy of +	** it, if it intends to use it later. +	*/ + +	void (*errhandler_func)(const char *errmsg, void *errmsg_arg); +	void *errhandler_arg; /* Passthru arg to errhandler_func */ + +	/* Additional, arbitrary, arguments to GnuPG */ + +	int argc; +	char **argv; + +	/* On exit, the following bits may be set: */ + +	int errstatus; + +#define LIBMAIL_ERR_VERIFYSIG 1 +#define LIBMAIL_ERR_DECRYPT 2 + +}; + +int libmail_gpg_signencode(int dosign, +			   int doencode, +			   /* +			   ** One of LIBMAIL_GPG_INDIVIDUAL or +			   ** LIBMAIL_GPG_ENCAPSULATE +			   */ +			   struct libmail_gpg_info *options); + +int libmail_gpg_decode(int mode, +		       /* +		       ** LIBMAIL_GPG_UNENCRYPT OR LIBMAIL_GPG_CHECKSIGN +		       */ +		       struct libmail_gpg_info *options); + + +	/* A convenient input_func, where vp is FILE * */ + +int libmail_gpg_inputfunc_readfp(char *buf, size_t cnt, void *vp); + +	/* Other functions: */ + +int libmail_gpg_cleanup(); +int libmail_gpg_has_gpg(const char *gpgdir); + +int libmail_gpg_genkey(const char *gpgdir, +		       const char *charset, +		       const char *name, +		       const char *addr, +		       const char *comment, +		       int skeylen, +		       int ekeylen, +		       unsigned expire, +		       char expire_unit, +		       const char *passphrase, + +		       int (*dump_func)(const char *, size_t, void *), +		       int (*timeout_func)(void *), +		       void *voidarg); + +struct gpg_list_info { +	const char *charset; +	const char *disabled_msg; +	const char *revoked_msg; +	const char *expired_msg; +	const char *group_msg; +	void *voidarg; +} ; + +int libmail_gpg_listkeys(const char *gpgdir, +		 int secret, +		 int (*callback_func)(const char *, const char *, +				      const char *, int, +				      struct gpg_list_info *), +		 int (*err_func)(const char *, size_t, void *), +		 struct gpg_list_info *); + +int libmail_gpg_listgroups(const char *gpgdir, +			   int (*callback_func)(const char *, const char *, +						const char *, +						int, +						struct gpg_list_info *), +			   struct gpg_list_info *voidarg); + +int libmail_gpg_exportkey(const char *gpgdir, +		  int secret, +		  const char *fingerprint, +		  int (*out_func)(const char *, size_t, void *), +		  int (*err_func)(const char *, size_t, void *), +		  void *voidarg); + +int libmail_gpg_deletekey(const char *gpgdir, int secret, const char *fingerprint, +		  int (*dump_func)(const char *, size_t, void *), +		  void *voidarg); + +int libmail_gpg_signkey(const char *gpgdir, const char *signthis, const char *signwith, +		int passphrase_fd, +		int (*dump_func)(const char *, size_t, void *), +		int trustlevel, +		void *voidarg); + +int libmail_gpg_checksign(const char *gpgdir, +		  const char *content,	/* Filename, for now */ +		  const char *signature, /* Filename, for now */ +		  int (*dump_func)(const char *, size_t, void *), +		  void *voidarg); + +	/* IMPORT A KEY */ + +int libmail_gpg_import_start(const char *gpgdir, int issecret); + +int libmail_gpg_import_do(const char *p, size_t n,	/* Part of the key */ +		  int (*dump_func)(const char *, size_t, void *), +		  /* gpg output callback */ + +		  void *voidarg); + +int libmail_gpg_import_finish(int (*dump_func)(const char *, size_t, void *), +		      void *voidarg); + + + +	     /* INTERNAL: */ + +pid_t libmail_gpg_fork(int *, int *, int *, const char *, char **); + +#define GPGARGV_PASSPHRASE_FD(argv,i,fd,buf) \ +	((argv)[(i)++]="--passphrase-fd", \ +	 (argv)[(i)++]=libmail_str_size_t((fd),(buf))) + +int libmail_gpg_write(const char *, size_t, +	      int (*)(const char *, size_t, void *), +	      int (*)(const char *, size_t, void *), +	      int (*)(void *), +	      unsigned, +	      void *); + +int libmail_gpg_read(int (*)(const char *, size_t, void *), +	     int (*)(const char *, size_t, void *), +	     int (*)(void *), +	     unsigned, +	     void *); + +char *libmail_gpg_options(const char *gpgdir); +	/* Filename of the options file.  If gpgdir is NULL try +	** the environment variables. */ + + +struct rfc2045 *libmail_gpgmime_is_multipart_signed(const struct rfc2045 *); +	/* +	** Return ptr to signed content if ptr is a multipart/signed. +	*/ + +struct rfc2045 *libmail_gpgmime_is_multipart_encrypted(const struct rfc2045 *); +	/* +	** Return ptr to encrypted content if ptr is a multipart/encrypted. +	*/ + +int libmail_gpgmime_has_mimegpg(const struct rfc2045 *); +	/* +	** Return non-zero if MIME content has any signed or encrypted +	** content. +	*/ + +int libmail_gpgmime_is_decoded(const struct rfc2045 *, int *); +	/* +	** Return non-zero if this is a multipart/mixed section generated +	** by mimegpg, and return the GnuPG return code. +	*/ + +struct rfc2045 *libmail_gpgmime_decoded_content(const struct rfc2045 *); +	/* +	** If is_decoded, then return the ptr to the decoded content. +	** (note - if decryption failed, NULL is returned). +	*/ + +struct rfc2045 *libmail_gpgmime_signed_content(const struct rfc2045 *); +	/* +	** If is_multipart_signed, return ptr to the signed content. +	*/ + +#ifdef  __cplusplus +} +#endif +#endif diff --git a/gpglib/import.c b/gpglib/import.c new file mode 100644 index 0000000..b3cdd38 --- /dev/null +++ b/gpglib/import.c @@ -0,0 +1,70 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + + +/* +** Import a key. +*/ + +int libmail_gpg_import_start(const char *gpgdir, int secret) +{ +	char *argvec[5]; + +	argvec[0]="gpg"; +	argvec[1]="--import"; +	argvec[2]="--no-tty"; +	argvec[3]=0; + +#if GPG_HAS_ALLOW_SECRET_KEY_IMPORT +	if (secret) +	{ +		argvec[3]="--allow-secret-key-import"; +		argvec[4]=0; +	} +#endif + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL, gpgdir, argvec) < 0) +		return (-1); +	return (0); +} + +int libmail_gpg_import_do(const char *p, size_t n, +		  int (*dump_func)(const char *, size_t, void *), +		  void *voidarg) +{ +	if (libmail_gpg_write(p, n, dump_func, dump_func, NULL, 0, voidarg)) +	{ +		libmail_gpg_cleanup(); +		return (-1); +	} +	return (0); +} + +int libmail_gpg_import_finish(int (*dump_func)(const char *, size_t, void *), +		      void *voidarg) +{ +	if (libmail_gpg_read(dump_func, dump_func, NULL, 0, voidarg)) +		return (-1); +	return (libmail_gpg_cleanup()); +} diff --git a/gpglib/libgpg.c b/gpglib/libgpg.c new file mode 100644 index 0000000..7bea385 --- /dev/null +++ b/gpglib/libgpg.c @@ -0,0 +1,104 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<signal.h> +#include	<errno.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#if	HAVE_SYS_TIME_H +#include	<sys/time.h> +#endif +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#include	"gpg.h" +#include	"gpglib.h" + +int libmail_gpg_stdin= -1, libmail_gpg_stdout= -1, +	libmail_gpg_stderr= -1; +pid_t libmail_gpg_pid= -1; + +int libmail_gpg_cleanup() +{ +	int rc=0; + +	if (libmail_gpg_stdin >= 0) +	{ +		close(libmail_gpg_stdin); +		libmail_gpg_stdin= -1; +	} + +	if (libmail_gpg_stdout >= 0) +	{ +		close(libmail_gpg_stdout); +		libmail_gpg_stdout= -1; +	} + +	if (libmail_gpg_stderr >= 0) +	{ +		close(libmail_gpg_stderr); +		libmail_gpg_stderr= -1; +	} + +	if (libmail_gpg_pid >= 0 && kill(libmail_gpg_pid, 0) >= 0) +	{ +		int waitstat; +		pid_t p; + +		while ((p=wait(&waitstat)) != libmail_gpg_pid) +		{ +			if (p < 0 && errno == ECHILD) +				return (-1); +		} + +		rc= WIFEXITED(waitstat) ? WEXITSTATUS(waitstat): -1; +		libmail_gpg_pid= -1; +	} +	return (rc); +} + +static char *optionfile(const char *gpgdir) +{ +	char *p=malloc(strlen(gpgdir)+sizeof("/options")); + +	if (p) +		strcat(strcpy(p, gpgdir), "/options"); +	return (p); +} + +/* +** Determine of GPG is available by checking for the options file, and the +** gpg binary itself. +*/ + +int libmail_gpg_has_gpg(const char *gpgdir) +{ +	char *p=optionfile(gpgdir); +	struct stat stat_buf; +	int rc; + +	if (!p) +		return (-1); + +	rc=stat(p, &stat_buf); +	free(p); +	if (rc == 0) +		rc=stat(GPG, &stat_buf); + +	return (rc); +} diff --git a/gpglib/list.c b/gpglib/list.c new file mode 100644 index 0000000..7b58355 --- /dev/null +++ b/gpglib/list.c @@ -0,0 +1,574 @@ +/* +** Copyright 2001-2011 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<signal.h> +#include	<ctype.h> +#include	<time.h> +#include	<errno.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + +/* +** List keys +*/ + +static int dolist(int (*)(const char *, const char *, const char *, int, +			  struct gpg_list_info *), +		  int (*)(const char *, size_t, void *), +		  void *); + +static void definit(struct gpg_list_info *arg) +{ + +#define DEFINIT(c, a) if (!(c)) (c)=(a) + +	DEFINIT(arg->charset, "iso-8859-1"); +	DEFINIT(arg->disabled_msg, "[ This key is disabled ]"); +	DEFINIT(arg->revoked_msg, "[ This key is revoked ]"); +	DEFINIT(arg->expired_msg, "[ This key is expired ]"); +	DEFINIT(arg->group_msg, "Group: @"); +} + +int libmail_gpg_listkeys(const char *gpgdir, +		 int secret, +		 int (*callback_func)(const char *, const char *, +				      const char *, +				      int, +				      struct gpg_list_info *), +		 int (*err_func)(const char *, size_t, void *), +		 struct gpg_list_info *voidarg) +{ +	char *argvec[7]; +	int rc; + +	argvec[0]="gpg"; +	argvec[1]= secret ? "--list-secret-keys":"--list-sigs"; +	argvec[2]="--with-colons"; +	argvec[3]="--fingerprint"; +	argvec[4]="-q"; +	argvec[5]="--no-tty"; +	argvec[6]=0; + +	definit(voidarg); + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, +			     &libmail_gpg_stderr, gpgdir, argvec) < 0) +		rc= -1; +	else +	{ +		int rc2; + +		rc=dolist(callback_func, err_func, voidarg); + +		rc2=libmail_gpg_cleanup(); +		if (rc2) +			rc=rc2; +	} +	return (rc); +} + +/* +** Parse output of gpg into physical lines. +*/ + +struct gpg_callbackinfo { +	int (*err_func)(const char *, size_t, void *); +	/* stderr passthrough */ + +	struct gpg_list_info *voidarg; +	/* passthrough arg */ + +	/* Line buffer: */ +	char *linebuffer; +	size_t linebufsize; +	size_t linebufcnt; + +	/* Function that receives collected lines, + passthrough arg */ +	int (*line_func)(char *, void *, struct gpg_list_info *); +	void *line_callback_arg; +} ; + +static int libmail_gpg_line_stderr(const char *l, size_t c, void *vp) +{ +	return (0); +} + +static int libmail_gpg_line_stdout(const char *l, size_t c, void *vp) +{ +	struct gpg_callbackinfo *gci=(struct gpg_callbackinfo *)vp; +	size_t i, j; + +	if (c + gci->linebufcnt >= gci->linebufsize) +	{ +		/* Need bigger line buffer */ + +		size_t news= c+gci->linebufcnt+256; + +		char *newp= gci->linebuffer ? realloc(gci->linebuffer, news) +			: malloc(news); + +		if (!newp) +			return (-1); +		gci->linebuffer=newp; +		gci->linebufsize=news; +	} + +	memcpy(gci->linebuffer + gci->linebufcnt, l, c); +	gci->linebufcnt += c; + +	/* Search for collected newlines, extract complete lines, +	** invoke the callback function. +	*/ + +	for (;;) +	{ +		int rc; + +		for (i=0; i<gci->linebufcnt; i++) +			if (gci->linebuffer[i] == '\n') +				break; +		if (i >= gci->linebufcnt) +			break; +		gci->linebuffer[i++]=0; + +		rc= (*gci->line_func)(gci->linebuffer, gci->line_callback_arg, +				      gci->voidarg); +		j=0; +		while (i < gci->linebufcnt) +		{ +			gci->linebuffer[j]=gci->linebuffer[i]; +			++i; +			++j; +		} +		gci->linebufcnt=j; +		if (rc) +			return (rc); +	} +	return (0); +} + +/* +** Parse list output of gpg into distrinct keys. +*/ + +struct gpg_callbacklistinfo { + +	int (*callback_func)(const char *, const char *, const char *, int, +			     struct gpg_list_info *); +	int invalid_flag; +	char fingerprint[128]; +	char shortname[256]; +	char *keybuffer; +	size_t keybufsize; +	size_t keybuflen; + +	int seen_startofkey; +} ; + +static int dolist_callback(char *, void *, struct gpg_list_info *); + +static int dolist(int (*callback_func)(const char *, const char *, +				       const char *, int, +				       struct gpg_list_info *), +		  int (*err_func)(const char *, size_t, void *), +		  void *voidarg) +{ +	struct gpg_callbackinfo gci; +	struct gpg_callbacklistinfo gcli; +	int rc, rc2; + +	close(libmail_gpg_stdin); +	libmail_gpg_stdin= -1; + +	memset(&gci, 0, sizeof(gci)); +	gci.err_func=err_func; +	gci.voidarg=voidarg; + +	gci.line_func= &dolist_callback; +	gci.line_callback_arg= &gcli; + +	memset(&gcli, 0, sizeof(gcli)); +	gcli.callback_func=callback_func; + +	rc=libmail_gpg_read( libmail_gpg_line_stdout, +			     libmail_gpg_line_stderr, +			     NULL, +			     0, +			     &gci); + + +	rc2= (*gci.line_func)(NULL, gci.line_callback_arg, +					  gci.voidarg); +	if (rc2) +		rc=rc2; + +	if (gcli.keybuffer) +		free(gcli.keybuffer); + +	if (gci.linebuffer) +		free(gci.linebuffer); +	return (rc); +} + +static char *nextword(char *); + +static int nyb(int c) +{ +	static const char xdigits[]="0123456789ABCDEFabcdef"; + +	char *p=strchr(xdigits, c); + +	if (!p) +		return (0); + +	c= p-xdigits; + +	if (c >= 16) +		c -= 6; +	return (c); +} + +static int append_key(struct gpg_callbacklistinfo *, const char *); +static int append_date(struct gpg_callbacklistinfo *, const char *); + +static int dolist_callback(char *line, void *vp1, +			   struct gpg_list_info *vp) +{ +	struct gpg_callbacklistinfo *gci=(struct gpg_callbacklistinfo *)vp1; + +	char *rectype; +	char *trust; +	char *length; +	int algo; +	/*char *keyid;*/ +	char *crdate; +	char *expdate; +	/*char *localid;*/ +	/*char *ownertrust;*/ +	char *userid; +	char *p; +	const char *stat; + +	if (!line || strncmp(line, "pub", 3) == 0 +	    || strncmp(line, "sec", 3) == 0) +	{ +		if (gci->seen_startofkey) +		{ +			int rc=(*gci->callback_func)(gci->fingerprint, +						     gci->shortname, +						     gci->keybuflen ? +						     gci->keybuffer:"", +						     gci->invalid_flag, +						     vp); +			gci->keybuflen=0; +			gci->fingerprint[0]=0; +			gci->shortname[0]=0; +			if (rc) +				return (rc); +		} + +		if (!line) +			return (0); + +		gci->seen_startofkey=1; +	} + +	if (!gci->seen_startofkey) +		return (0); + +	p=line; +	rectype=p; p=nextword(p); +	trust=p; p=nextword(p); +	length=p; p=nextword(p); +	algo=atoi(p); p=nextword(p); +	/*keyid=p;*/ p=nextword(p); +	crdate=p; p=nextword(p); +	expdate=p; p=nextword(p); +	/*localid=p;*/ p=nextword(p); +	/*ownertrust=p;*/ p=nextword(p); +	userid=p; p=nextword(p); + +	{ +		char *q; + +		for (p=q=userid; *p; ) +		{ +			int n; + +			if (*p != '\\') +			{ +				*q=*p; +				++p; +				++q; +				continue; +			} +			++p; +			if (*p != 'x') +			{ +				*q=*p; +				if (*p) +					++p; +				++q; +				continue; +			} +			++p; +			n=nyb(*p); +			if (*p) +				++p; +			n=n * 16 + nyb(*p); +			if (*p) +				++p; +			*q=n; +			++q; +		} +		*q=0; +	} + +	stat=0; + +	if (strcmp(rectype, "fpr") == 0) +	{ +		gci->fingerprint[0]=0; +		strncat(gci->fingerprint, userid, +			sizeof(gci->fingerprint)-2); +		return (0); +	} + +	if (strcmp(rectype, "pub") == 0 || +	    strcmp(rectype, "sec") == 0) +	{ +		stat= *trust == 'd' ? vp->disabled_msg: +			*trust == 'r' ? vp->revoked_msg: +			*trust == 'e' ? vp->expired_msg:0; + +	} +	else if (strcmp(rectype, "sub") && +		 strcmp(rectype, "ssb") && +		 strcmp(rectype, "uid") && +		 strcmp(rectype, "sig")) +		return (0); + +	if (append_key(gci, rectype) || +	    append_key(gci, " ")) +		return (-1); + + +	gci->invalid_flag= stat ? 1:0; + +	{ +		char buf[60]; + +		sprintf(buf, "%4.4s/%-8.8s", length, +			algo == 1 ? "RSA": +			algo == 16 ? "ElGamal": +			algo == 17 ? "DSA": +			algo == 20 ? "DSA":"???"); + +		if (algo == 0 || *length == 0) +			sprintf(buf, "%13s", ""); + +		if (append_key(gci, buf)) +			return (-1); +	} + +	userid=libmail_u_convert_fromutf8(userid, vp->charset, NULL); +	if (!userid) +		return (-1); + +	if (strcmp(rectype, "pub") == 0 || +	    strcmp(rectype, "sec") == 0 || +	    (strcmp(rectype, "uid") == 0 && gci->shortname[0] == 0)) +	{ +		gci->shortname[0]=0; +		strncat(gci->shortname, userid, +			sizeof(gci->shortname)-2); + +	} + +	if (append_key(gci, " ") +	    || append_date(gci, crdate) +	    || append_key(gci, " ") +	    || append_date(gci, expdate) +	    || append_key(gci, " ") +	    || append_key(gci, userid) +	    || append_key(gci, "\n")) +	{ +		free(userid); +		return (-1); +	} +	free(userid); + +	if (stat) +	{ +		append_key(gci, "                  "); +		append_key(gci, stat); +		append_key(gci, "\n"); +	} + +	return (0); +} + +static char *nextword(char *p) +{ +	while (*p && *p != ':') +		++p; + +	if (*p) +		*p++=0; +	return (p); +} + +static int append_date(struct gpg_callbacklistinfo *gci, const char *dtval) +{ +	char buf[20]; + +	const char *t; +	struct tm tmbuf; +	time_t secs; + +	if (strlen(dtval) == 10 && strchr(dtval, '-')) +		return append_key(gci, dtval); /* YYYY-MM-DD */ + +	t=strchr(dtval, 'T'); + +	if (t && (t-dtval) == 8) /* YYYYMMDDThhmmss */ +	{ +		sprintf(buf, "%.4s-%.2s-%.2s", dtval, dtval+4, dtval+6); +		return append_key(gci, buf); +	} + +	secs=0; +	while (dtval) +	{ +		if (*dtval < '0' || *dtval > '9') +			break; +		secs = secs * 10 + (*dtval++ - '0'); +	} + +	if (secs == 0 || *dtval || localtime_r(&secs,  &tmbuf) == NULL) +		return append_key(gci, "          "); + +	buf[strftime(buf, sizeof(buf)-1, "%Y-%m-%d", &tmbuf)]=0; + +	return append_key(gci, buf); +} + +static int append_key(struct gpg_callbacklistinfo *gci, const char *l) +{ +	int ll=strlen(l); + +	if (ll + gci->keybuflen >= gci->keybufsize) +	{ +		int n=ll + gci->keybuflen + 256; + +		char *p= gci->keybuffer ? realloc(gci->keybuffer, n) +			: malloc(n); +		if (!p) +			return (-1); +		gci->keybuffer=p; +		gci->keybufsize=n; +	} +	strcpy(gci->keybuffer + gci->keybuflen, l); +	gci->keybuflen += ll; +	return (0); +} + +int libmail_gpg_listgroups(const char *gpgdir, +			   int (*callback_func)(const char *, const char *, +						const char *, +						int, +						struct gpg_list_info *), +			   struct gpg_list_info *voidarg) +{ +	char *filename=libmail_gpg_options(gpgdir); +	FILE *fp; +	char buf[BUFSIZ]; +	char *p; +	char *q; +	char *r; +	int rc; + +	definit(voidarg); + +	if (!filename) +		return 0; + +	fp=fopen(filename, "r"); +	free(filename); + +	if (!fp) +		return 0; + +	while (fgets(buf, sizeof(buf), fp)) +	{ +		if (strncmp(buf, "group", 5) || +		    !isspace((int)(unsigned char)buf[5])) +			continue; + +		for (p=buf+5; *p && isspace((int)(unsigned char)*p); ++p) +			; +		q=strchr(p, '='); +		if (!q) +			continue; +		*q=0; + + +		/* strip trailing spaces */ + +		for (q=r=p; *q; q++) +			if (!isspace((int)(unsigned char)*q)) +				r=q+1; +		*r=0; + +		if (*p == 0) +			continue; + +		q=libmail_u_convert_fromutf8(p, voidarg->charset, NULL); + +		if (!q) +			continue; + +		r=malloc(strlen(q)+strlen(voidarg->group_msg)+1); + +		if (!r) +		{ +			free(q); +			continue; +		} + +		strcpy(r, voidarg->group_msg); +		if ((p=strchr(r, '@')) != 0) +			strcat(strcpy(p, q), +			       strchr(voidarg->group_msg, '@')+1); + + +		rc=(*callback_func)(p, r, r, 0, voidarg); +		free(q); +		free(r); +		if (rc) +		{ +			fclose(fp); +			return rc; +		} +	} +	fclose(fp); +	return (0); +} diff --git a/gpglib/mimegpg.c b/gpglib/mimegpg.c new file mode 100644 index 0000000..e7a8ead --- /dev/null +++ b/gpglib/mimegpg.c @@ -0,0 +1,189 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + +#include "config.h" +#include "gpglib.h" +#include <stdio.h> +#include <stdlib.h> +#include <locale.h> +#include <string.h> +#include <signal.h> + +extern void libmail_gpg_noexec(int fd); + +void rfc2045_error(const char *p) +{ +	fprintf(stderr, "%s\n", p); +	exit(1); +} + +static void usage() +{ +	fprintf(stderr, "Usage: mimegpg [-s] [-e] [-c] [-d] -- [gpg options]\n"); +} + +static void my_output(const char *p, size_t n, +		      void *dummy) +{ +	FILE *fp=(FILE *)dummy; + +	if (fwrite(p, n, 1, fp) != 1) +	{ +		perror("write"); +		exit(1); +	} +} + +static int my_inputfunc(char *buf, size_t buf_size, void *vp) +{ +	FILE *fp=(FILE *)vp; +	size_t n; + +	if (buf_size <= 0) +		return -1; + +	--buf_size; + +	for (n=0; n<buf_size; n++) +	{ +		int c=fgetc(fp); + +		if (c == EOF) +		{ +			buf[n]=0; + +			return n ? 0:-1; +		} + +		buf[n]=c; + +		if (c == '\n') +		{ +			n++; +			break; +		} +	} +	buf[n]=0; +	return 0; +} + + +static void my_errhandler(const char *msg, void *dummy) +{ +	fprintf(stderr, "ERROR: %s\n", msg); +} + +int main(int argc, char **argv) +{ +	int argn; +	int dosign=0; +	int doencode=0; +	int dodecode=0; +	int rc; +	const char *passphrase_fd=0; +	struct libmail_gpg_info gpg_info; + +	setlocale(LC_ALL, "C"); + +	for (argn=1; argn < argc; argn++) +	{ +		if (strcmp(argv[argn], "--") == 0) +		{ +			++argn; +			break; +		} + +		if (strcmp(argv[argn], "-s") == 0) +		{ +			dosign=1; +			continue; +		} + +		if (strcmp(argv[argn], "-e") == 0) +		{ +			doencode=LIBMAIL_GPG_INDIVIDUAL; +			continue; +		} + +		if (strcmp(argv[argn], "-E") == 0) +		{ +			doencode=LIBMAIL_GPG_ENCAPSULATE; +			continue; +		} + +		if (strcmp(argv[argn], "-d") == 0) +		{ +			dodecode |= LIBMAIL_GPG_UNENCRYPT; +			continue; +		} + +		if (strcmp(argv[argn], "-c") == 0) +		{ +			dodecode |= LIBMAIL_GPG_CHECKSIGN; +			continue; +		} + +		if (strcmp(argv[argn], "-p") == 0) +		{ +			++argn; +			if (argn < argc) +			{ +				passphrase_fd=argv[argn]; +				continue; +			} +			--argn; +		} + +		fprintf(stderr, "Unknown option: %s\n", argv[argn]); +		exit(1); +	} + +	if (!dosign && !doencode && !dodecode) +	{ +		usage(); +		return (1); +	} + +	signal(SIGCHLD, SIG_DFL); +	signal(SIGPIPE, SIG_IGN); + +	/* Make things sane */ + +	if (dosign || doencode) +		dodecode=0; + +#if 0 +	if (dosign && !doencode) +		reformime(argv[0]); +#endif + +	memset(&gpg_info, 0, sizeof(gpg_info)); + +	gpg_info.input_func=my_inputfunc; +	gpg_info.input_func_arg=stdin; +	gpg_info.output_func=my_output; +	gpg_info.output_func_arg=stdout; +	gpg_info.errhandler_func=my_errhandler; +	gpg_info.errhandler_arg=NULL; +	gpg_info.passphrase_fd=passphrase_fd; +	gpg_info.argc=argc - argn; +	gpg_info.argv=argv + argn; + +	libmail_gpg_noexec(fileno(stdin)); +	libmail_gpg_noexec(fileno(stdout)); + +	rc=dodecode ? libmail_gpg_decode(dodecode, &gpg_info) +		: libmail_gpg_signencode(dosign, doencode, &gpg_info); + + +	if (rc == 0 && (fflush(stdout) || ferror(stdout))) +	{ +		perror("write"); +		rc=1; +	} + +	exit(rc); +	return (0); +} diff --git a/gpglib/mimegpg.sgml b/gpglib/mimegpg.sgml new file mode 100644 index 0000000..3bce16f --- /dev/null +++ b/gpglib/mimegpg.sgml @@ -0,0 +1,152 @@ +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> +<!-- Copyright 1998 - 2008 Double Precision, Inc.  See COPYING for --> +<!-- distribution information. --> +<refentry> +  <info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info> + +  <refmeta> +    <refentrytitle>mimegpg</refentrytitle> +    <manvolnum>1</manvolnum> +    <refmiscinfo>Double Precision, Inc.</refmiscinfo> +  </refmeta> + +  <refnamediv> +    <refname>mimegpg</refname> +    <refpurpose>MIME-GPG utility</refpurpose> +  </refnamediv> + +  <refsynopsisdiv> +    <cmdsynopsis sepchar=" "> +      <command moreinfo="none">mimegpg</command> +      <arg choice="opt" rep="norepeat">-s</arg> +      <arg choice="opt" rep="norepeat">-E</arg> +      <arg choice="opt" rep="norepeat">-e</arg> +      <arg choice="opt" rep="norepeat">-c</arg> +      <arg choice="opt" rep="norepeat">-d</arg> +      <arg choice="opt" rep="norepeat">-p <replaceable>n</replaceable></arg> +      <arg choice="req" rep="norepeat">--</arg> +      <arg rep="repeat" choice="req">gpg options</arg> + +    </cmdsynopsis> +  </refsynopsisdiv> + +  <refsect1> +    <title>DESCRIPTION</title> + +    <para> +The <command moreinfo="none">mimegpg</command> tool signs, encrypts, or decrypts MIME-formatted E-mail +messages using GnuPG. <command moreinfo="none">mimegpg</command> does not contain any encryption code. It +uses the GnuPG utility for all encryption and decryption functions.</para> + +    <para> +The <option>-s</option> option signs an E-mail message. The <option>-E</option> or +the <option>-e</option> option encrypts the E-mail message. Specifying both +<option>-E/-e</option> and <option>-s</option> encrypts and signs the E-mail message +in a single step. The <option>-d</option> option decrypts the message. The +<option>-c</option> option checks signatures.</para> + +    <para> +<command moreinfo="none">mimegpg</command> works as a filter.  It reads an E-mail message from +standard input, which must be a MIME-formatted message. <command moreinfo="none">mimegpg</command> signs, +encrypts, and/or decrypts the message; then writes the encrypted, signed, or +decrypted MIME message on standard output.</para> + +    <para> +The standard input to <command moreinfo="none">mimegpg</command> must be a MIME E-mail message, with a +"<literal moreinfo="none">Mime Version: 1.0</literal>" header - even if the message does not contain any +attachments.  If the message contains any attachments, they are also signed +and/or encrypted, individually.</para> + +    <para> +<command moreinfo="none">mimegpg</command> automatically runs GnuPG, with the required options. +<command moreinfo="none">mimegpg</command>'s options may also be followed by a single <literal moreinfo="none">--</literal> +option; any remaining command line options are passed as additional options +to GnuPG. The <option>-E/-e</option> option usually requires at least one +<option>-r</option> GnuPG option, which may be specified in this fashion.</para> + +    <para> +The <option>-p</option> option specifies a file descriptor that contains any +required passphrase. Any other valid GnuPG options may follow a double-dash, +"--", as long as it makes sense for this operation (note that <command moreinfo="none">mimegpg</command> +automatically adds any GnuPG options that are needed to perform the given +operation). The "--no-tty" option can be useful when <command moreinfo="none">mimegpg</command> is +used in a non-interactive mode.  As always, secret keys that are +password-protected secret keys cannot be used in the <option>--no-tty</option> +mode, unless the <option>-p</option> option is used.</para> + +    <refsect2> +      <title>SIGNING AND ENCRYPTING MESSAGES</title> + +      <para> +Use the <option>-s</option> option to sign MIME message content. Use the +<option>-E</option> option to encrypt it. Use both options to both sign and +encrypt. Follow with <literal moreinfo="none">--</literal>, then any other GnuPG options. The +<option>-E</option> option will require at least one <option>-r</option> GnuPG +option.</para> + +      <para> +The <option>-E</option> option encapsulates the message content and all the +attachments into a single encrypted MIME object. Some mail software cannot +handle encapsulated content. The <option>-e</option> option encrypts each +attachment separately, without encapsulation.</para> + +      <para> +If a secret key used for signing is password protected, the prompt to +enter the password will be issued directly by GnuPG. Note that the +<option>-s</option> and the <option>-e</option> (but not <option>-E</option>) options may +issue multiple password prompts in interactive mode. <command moreinfo="none">mimegpg</command> runs +GnuPG multiple times if the MIME message contains attachments.  GnuPG will be +invoked separately for each attachment in the MIME message, and each +invocation will prompt for any required key password.  Note that the initial +message headers are not signed and/or encrypted, except for the MIME headers +themselves.  However, any <literal moreinfo="none">message/rfc822</literal> MIME content - attached +messages - are encrypted/signed in their entirety, headers and content.</para> +    </refsect2> + +    <refsect2> +      <title>DECRYPTING AND CHECKING SIGNATURES</title> + +      <para> +The <option>-d</option> option attempts to decrypt any encrypted content in a +MIME message.  The <option>-c</option> option attempts to verify signatures of +any signed content.  Both <option>-c</option> and <option>-d</option> +can be specified at the +same time. <option>-d</option> looks for any <literal moreinfo="none">multipart/encrypted</literal> +MIME content, then attempts to decrypt it. <option>-c</option> looks for any +<literal moreinfo="none">multipart/signed</literal> MIME content, then attempts to verify the +signature.</para> + +      <para> +The <option>-c</option> and <option>-d</option> options replace the +<literal moreinfo="none">multipart/signed</literal> and <literal moreinfo="none">multipart/encrypted</literal> MIME +content with a <literal moreinfo="none">multipart/x-mimepgp</literal> section, that contains an +additional attribute called "xpgpstatus". The value of the attribute is set +to the exit code of GnuPG. Succesfully decrypting the message and/or +verifying the signature sets the exit code to 0. A non-zero exit code +indicates that the signature could not be verified, or the message could not +be decrypted.</para> + +      <para> +The first section in this <literal moreinfo="none">multipart/x-mimepgp</literal> is a +<literal moreinfo="none">text/plain</literal> section that contains any messages from GnuPG. The +second section is any decrypted or signed content. <command moreinfo="none">mimegpg</command> will +include the signed content even if the signature could not be verified (check +<literal moreinfo="none">xpgpstatus</literal>). Encrypted content that could not be decrypted will +not be included (obviously).</para> + +      <para> +Note - any existing x-mimegpg MIME section will have its content-type +quietly reset to multipart/mixed, in order to avoid confusion (except when +this is what got decrypted).</para> + +    </refsect2> +  </refsect1> + +  <refsect1> +    <title>SEE ALSO</title> + +    <para> +<ulink url="reformime.html"><citerefentry><refentrytitle>reformime</refentrytitle><manvolnum>1</manvolnum></citerefentry></ulink>.</para> +  </refsect1> + +</refentry> diff --git a/gpglib/mimegpgfork.c b/gpglib/mimegpgfork.c new file mode 100644 index 0000000..d1b849c --- /dev/null +++ b/gpglib/mimegpgfork.c @@ -0,0 +1,449 @@ +/* +** Copyright 2001-2010 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include "gpg.h" + +#include <sys/types.h> +#if	HAVE_SYS_TIME_H +#include	<sys/time.h> +#endif +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#include	"mimegpgfork.h" + +static int libmail_gpgmime_fork(const char *gpgdir, +			const char *passphrase_fd, +			int xargc, char **xargv, int argc, char **argv, +			struct gpgmime_forkinfo *fi) +{ +	int pipes[3][2]; +	int n; +	pid_t p; +	struct sigaction sa; + +	for (n=0; n<3; n++) +	{ +		if (pipe(pipes[n]) < 0) +		{ +			while (n) +			{ +				--n; +				close(pipes[n][0]); +				close(pipes[n][1]); +				return (-1); +			} +		} +	} + +	if (fcntl(pipes[0][1], F_SETFL, O_NONBLOCK) || +	    fcntl(pipes[1][0], F_SETFL, O_NONBLOCK) || +	    fcntl(pipes[2][0], F_SETFL, O_NONBLOCK)) +	{ +		for (n=0; n<3; n++) +		{ +			close(pipes[n][0]); +			close(pipes[n][1]); +		} +		return (-1); +	} + +	signal(SIGCHLD, SIG_DFL); + +	p=fork(); +	if (p < 0) +	{ +		for (n=0; n<3; n++) +		{ +			close(pipes[n][0]); +			close(pipes[n][1]); +		} +		return (-1); +	} + +	if (p == 0) +	{ +		char **newargv; +		int i; +		const char *gpg; + +		dup2(pipes[0][0], 0); +		dup2(pipes[1][1], 1); +		dup2(pipes[2][1], 2); + +		for (n=0; n<3; n++) +		{ +			close(pipes[n][0]); +			close(pipes[n][1]); +		} + +		newargv=malloc( (xargc + argc + 5) * sizeof(char *)); +		if (!newargv) +		{ +			perror("malloc"); +			_exit(1); +		} + +		i=0; +		newargv[i++]="gpg"; +		if (passphrase_fd) +		{ +			int n=atoi(passphrase_fd); + +			if (lseek(n, 0L, SEEK_SET) < 0) +			{ +				perror("passphrase-fd: seek"); +				_exit(1); +			} + +			newargv[i++]="--batch"; +			newargv[i++]="--passphrase-fd"; +			newargv[i++]=(char *)passphrase_fd; +		} + +		for (n=0; n<xargc; n++) +			newargv[i++]=xargv[n]; +		for (n=0; n<argc; n++) +			newargv[i++]=argv[n]; + +		newargv[i]=0; + +		if (gpgdir) +		{ +			char *s; + +			s=malloc(sizeof("GNUPGHOME=")+strlen(gpgdir)); +			if (!s) +			{ +				perror("malloc"); +				exit(1); +			} +			strcat(strcpy(s, "GNUPGHOME="), gpgdir); +			if (putenv(s) < 0) +			{ +				perror("putenv"); +				exit(1); +			} +		} + +		gpg=getenv("GPG"); +		if (!gpg || !*gpg) +			gpg=GPG; + +		execvp(gpg, newargv); +		perror(gpg); +		_exit(1); +	} + +	fi->gpg_errflag=0; +	fi->togpg_fd=pipes[0][1]; +	close(pipes[0][0]); +	fi->fromgpg_fd=pipes[1][0]; +	close(pipes[1][1]); +	fi->fromgpg_errfd=pipes[2][0]; +	close(pipes[2][1]); + +	fi->gpg_writecnt=0; +	fi->gpg_errcnt=0; +	fi->gpg_errbuf[0]=0; +	fi->gpg_pid=p; + +	memset(&sa, 0, sizeof(sa)); +	sa.sa_handler=SIG_IGN; +	sigaction(SIGPIPE, &sa, &fi->old_pipe_sig); + +	fi->gpg_readhandler=0; +	fi->gpg_voidarg=0; +	return (0); +} + +static void gpgmime_writeflush(struct gpgmime_forkinfo *); + +void libmail_gpgmime_write(struct gpgmime_forkinfo *fi, const char *p, size_t n) +{ +	while (n) +	{ +		size_t i; + +		if (fi->gpg_writecnt == sizeof(fi->gpg_writebuf)) +			gpgmime_writeflush(fi); + +		i=sizeof(fi->gpg_writebuf) - fi->gpg_writecnt; + +		if ((size_t)i > n) +			i=n; + +		memcpy(fi->gpg_writebuf + fi->gpg_writecnt, p, i); + +		fi->gpg_writecnt += i; + +		p += i; +		n -= i; +	} +} + +static void libmail_gpgmime_read(struct gpgmime_forkinfo *fi, fd_set *fdr) +{ +	char readbuf[BUFSIZ]; +	int i; + +	if (fi->fromgpg_fd >= 0 && FD_ISSET(fi->fromgpg_fd, fdr)) +	{ +		i=read(fi->fromgpg_fd, readbuf, sizeof(readbuf)); + +		if (i <= 0) +		{ +			close(fi->fromgpg_fd); +			fi->fromgpg_fd= -1; +		} +		else +		{ +			if (fi->gpg_readhandler) +				fi->gpg_errflag= +					(*fi->gpg_readhandler)(readbuf, i, +							       fi->gpg_voidarg +							       ); +		} +	} + +	if (fi->fromgpg_errfd >= 0 && FD_ISSET(fi->fromgpg_errfd, fdr)) +	{ +		i=read(fi->fromgpg_errfd, readbuf, sizeof(readbuf)); + +		if (i <= 0) +		{ +			close(fi->fromgpg_errfd); +			fi->fromgpg_errfd= -1; +		} +		else +		{ +			if (i >= sizeof(fi->gpg_errbuf)-1-fi->gpg_errcnt) +				i=sizeof(fi->gpg_errbuf)-1-fi->gpg_errcnt; +			if (i) +			{ +				memcpy(fi->gpg_errbuf+fi->gpg_errcnt, +				       readbuf, i); +				fi->gpg_errbuf[fi->gpg_errcnt += i]=0; +			} +		} +	} +} + +static void gpgmime_writeflush(struct gpgmime_forkinfo *fi) +{ +	const char *p=fi->gpg_writebuf; +	unsigned n=fi->gpg_writecnt; + +	while (n && !fi->gpg_errflag) +	{ +		fd_set fdr, fdw; +		int maxfd=fi->togpg_fd, i; + +		FD_ZERO(&fdr); +		FD_ZERO(&fdw); + +		FD_SET(fi->togpg_fd, &fdw); + +		if (fi->fromgpg_fd >= 0) +		{ +			FD_SET(fi->fromgpg_fd, &fdr); +			if (fi->fromgpg_fd > maxfd) +				maxfd=fi->fromgpg_fd; +		} + +		if (fi->fromgpg_errfd >= 0) +		{ +			FD_SET(fi->fromgpg_errfd, &fdr); +			if (fi->fromgpg_errfd > maxfd) +				maxfd=fi->fromgpg_errfd; +		} + +		if (select(maxfd+1, &fdr, &fdw, NULL, NULL) <= 0) +			continue; + +		libmail_gpgmime_read(fi, &fdr); + +		if (!FD_ISSET(fi->togpg_fd, &fdw)) +			continue; + +		i=write(fi->togpg_fd, p, n); + +		if (i <= 0) +			fi->gpg_errflag=1; +		else +		{ +			p += i; +			n -= i; +		} +	} +	fi->gpg_writecnt=0; +} + +int libmail_gpgmime_finish(struct gpgmime_forkinfo *fi) +{ +	pid_t p2; +	int waitstat; + +	gpgmime_writeflush(fi); +	close(fi->togpg_fd); +	sigaction(SIGPIPE, &fi->old_pipe_sig, NULL); + +	while (!fi->gpg_errflag && +	       (fi->fromgpg_fd >= 0 || fi->fromgpg_errfd >= 0)) +	{ +		fd_set fdr; +		int maxfd=0; + +		FD_ZERO(&fdr); + +		if (fi->fromgpg_fd >= 0) +		{ +			FD_SET(fi->fromgpg_fd, &fdr); +			if (fi->fromgpg_fd > maxfd) +				maxfd=fi->fromgpg_fd; +		} + +		if (fi->fromgpg_errfd >= 0) +		{ +			FD_SET(fi->fromgpg_errfd, &fdr); +			if (fi->fromgpg_errfd > maxfd) +				maxfd=fi->fromgpg_errfd; +		} + +		if (select(maxfd+1, &fdr, NULL, NULL, NULL) <= 0) +			continue; + +		libmail_gpgmime_read(fi, &fdr); +	} + +	while ((p2=wait(&waitstat)) != fi->gpg_pid) +	{ +		if (p2 < 0 && errno == ECHILD) +			break; +	} + +	if (fi->gpg_errflag == 0) +	{ +		if (!WIFEXITED(waitstat)) +			fi->gpg_errflag=1; +		else +			fi->gpg_errflag=WEXITSTATUS(waitstat); +	} +	return (fi->gpg_errflag); +} + +int libmail_gpgmime_forksignencrypt(const char *gpgdir, +				    const char *passphrase_fd, +				    int flags, +				    int argc, char **argv, +				    int (*output_func)(const char *, +						       size_t, void *), +				    void *output_voidarg, +				    struct gpgmime_forkinfo *gpgfi) +{ +	char *xargvec[5]; +	int xargc=0; +	int rc; + +	if (flags & GPG_SE_SIGN) +	{ +		xargvec[xargc++]="-s"; +		if (! (flags & GPG_SE_ENCRYPT)) +			xargvec[xargc++]="-b"; +	} + +	if (flags & GPG_SE_ENCRYPT) +		xargvec[xargc++]="-e"; + +	xargvec[xargc++]="-a"; + +	rc=libmail_gpgmime_fork(gpgdir, passphrase_fd, +			xargc, xargvec, argc, argv, gpgfi); + +	gpgfi->gpg_readhandler= output_func; +	gpgfi->gpg_voidarg=output_voidarg; +	return (rc); +} + +int libmail_gpgmime_forkchecksign(const char *gpgdir, +			  const char *passphrase_fd, +			  const char *content_filename, +			  const char *signature_filename, +			  int argc, char **argv, +			  struct gpgmime_forkinfo *gpgfi) +{ +	char *xargvec[3]; +	char **newargv; +	int i; +	int rc; + +	xargvec[0]="--verify"; +	xargvec[1]="--charset"; +	xargvec[2]=GPG_CHARSET; + +	newargv=(char **)malloc((argc+2)*sizeof(char *)); +	if (!newargv) +	{ +		perror("malloc"); +		exit(1); +	} + +	for (i=0; i<argc; i++) +		newargv[i]=argv[i]; +	newargv[i++]=(char *)signature_filename; +	newargv[i++]=(char *)content_filename; + +	rc=libmail_gpgmime_fork(gpgdir, passphrase_fd, 3, xargvec, i, newargv, gpgfi); +	free(newargv); +	return (rc); +} + +int libmail_gpgmime_forkdecrypt(const char *gpgdir, +			const char *passphrase_fd, +			int argc, char **argv, +			int (*output_func)(const char *, size_t, void *), +			void *output_voidarg, +			struct gpgmime_forkinfo *gpgfi) +{ +	char *xargv[3]; +	int rc; + +	xargv[0]="--decrypt"; +	xargv[1]="--charset"; +	xargv[2]=GPG_CHARSET; + +	rc=libmail_gpgmime_fork(gpgdir, passphrase_fd, 3, xargv, argc, argv, gpgfi); + +	gpgfi->gpg_readhandler= output_func; +	gpgfi->gpg_voidarg= output_voidarg; +	return (rc); +} + +const char *libmail_gpgmime_getoutput(struct gpgmime_forkinfo *gpgfi) +{ +	return (gpgfi->gpg_errbuf); +} + +const char *libmail_gpgmime_getcharset(struct gpgmime_forkinfo *gpgfi) +{ +	return (GPG_CHARSET); +} diff --git a/gpglib/mimegpgfork.h b/gpglib/mimegpgfork.h new file mode 100644 index 0000000..cebee60 --- /dev/null +++ b/gpglib/mimegpgfork.h @@ -0,0 +1,84 @@ +#ifndef mimegpgfork_h +#define mimegpgfork_h + +/* +** Copyright 2001 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> + +#ifdef  __cplusplus +extern "C" { +#endif + +struct gpgmime_forkinfo { +	int togpg_fd; +	int fromgpg_fd; +	int fromgpg_errfd; + +	char gpg_writebuf[BUFSIZ]; +	char gpg_errbuf[1024]; + +	unsigned gpg_writecnt; +	unsigned gpg_errcnt; + +	int gpg_errflag; +	pid_t gpg_pid; + +	struct sigaction old_pipe_sig; + +	int (*gpg_readhandler)(const char *, size_t, void *); +	void *gpg_voidarg; +} ; + +int libmail_gpgmime_forksignencrypt(const char *,	/* gpgdir */ +				    const char *,	/* passphrase fd */ +				    int,	/* Flags: */ + +#define GPG_SE_SIGN	1 +#define	GPG_SE_ENCRYPT	2 + +				    int, char **,	/* argc/argv */ + +				    int (*)(const char *, size_t, void *), +				    /* Encrypted output */ +				    void *, /* 3rd arg to encrypted output */ + +				    struct gpgmime_forkinfo * +				    /* Allocated struct */ +				    ); + +int libmail_gpgmime_forkchecksign(const char *,	/* gpgdir */ +				  const char *,	/* passphrase fd */ +				  const char *,	/* content filename */ +				  const char *,	/* signature filename */ +				  int, char **,	/* argc/argv */ +				  struct gpgmime_forkinfo *); +	/* Allocated struct */ + +int libmail_gpgmime_forkdecrypt(const char *,	/* gpgdir */ +				const char *,	/* passphrase fd */ +				int, char **,	/* argc/argv */ +				int (*)(const char *, size_t, void *), +				/* Output callback function */ +				void *,	/* 3rd arg to callback function */ + +				struct gpgmime_forkinfo *); +	/* Allocated struct */ + +void libmail_gpgmime_write(struct gpgmime_forkinfo *, const char *, size_t); +int libmail_gpgmime_finish(struct gpgmime_forkinfo *); + +const char *libmail_gpgmime_getoutput(struct gpgmime_forkinfo *); +const char *libmail_gpgmime_getcharset(struct gpgmime_forkinfo *); + +#ifdef  __cplusplus +} ; +#endif + +#endif diff --git a/gpglib/mimegpgheader.c b/gpglib/mimegpgheader.c new file mode 100644 index 0000000..cb04e13 --- /dev/null +++ b/gpglib/mimegpgheader.c @@ -0,0 +1,393 @@ +/* +** Copyright 2001 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include "mimegpgheader.h" + +void libmail_readheader_init(struct read_header_context *cts) +{ +	cts->continue_header=0; +	cts->header_len=0; +	cts->first=0; +	cts->last=0; +} + +int libmail_readheader(struct read_header_context *cts, const char *buf) +{ +	int l; +	char *s; +	struct header *p; + +	l=strlen(buf); +	p=0; + +	if (cts->continue_header) +		p=cts->last; +	else if (isspace((int)(unsigned char)buf[0])) +		p=cts->last; + +	if (!p) +	{ +		p=(struct header *)malloc(sizeof(*p)); +		if (!p) +			return -1; + +		p->next=0; +		p->header=0; +		cts->header_len=0; +		if (cts->last) +			cts->last->next=p; +		else +			cts->first=p; +		cts->last=p; +	} + +	s=p->header ? realloc(p->header, cts->header_len + l + 1) +		:malloc(l+1); + +	if (!s) +		return (-1); + +	strcpy(s+cts->header_len, buf); +	cts->header_len += l; +	p->header=s; + +	cts->continue_header=!l || buf[l-1] != '\n'; + +	return 0; +} + +struct header *libmail_readheader_finish(struct read_header_context *cts) +{ +	struct header *last; + +	for (last=cts->first; last; last=last->next) +	{ +		char *q, *r; + +		if (!last->header) +			continue; + +		for (q=r=last->header; *r; r++) +		{ +			if (*r == '\r') +				continue; +			*q++ = *r; +		} +		*q=0; +	} +	return (cts->first); +} + +void libmail_header_free(struct header *p) +{ +	struct header *q; + +	while (p) +	{ +		q=p->next; +		if (p->header) +			free(p->header); +		free(p); +		p=q; +	} +} + +struct header *libmail_header_find(struct header *p, const char *n) +{ +	int l=strlen(n); + +	while (p) +	{ +		if (p->header && strncasecmp(p->header, n, l) == 0) +			return (p); +		p=p->next; +	} +	return (p); +} + +const char *libmail_header_find_txt(struct header *p, const char *n) +{ +	p=libmail_header_find(p, n); +	if (p) +		return (p->header+strlen(n)); +	return (NULL); +} + +void libmail_mimeheader_free(struct mime_header *m) +{ +	struct mime_attribute *a; + +	while ((a=m->attr_list) != 0) +	{ +		m->attr_list=a->next; +		if (a->name) +			free(a->name); +		if (a->value) +			free(a->value); +		free(a); +	} +	free(m->header_name); +	free(m); +} + +static void striptrlspc(char *p) +{ +	char *q; + +	for (q=p; *p; p++) +		if (!isspace((int)(unsigned char)*p)) +			q=p+1; +	*q=0; +} + +struct mime_header *libmail_mimeheader_parse(const char *field) +{ +	struct mime_header *header=0; +	struct mime_attribute *last_attr=0; + +	while (*field || header == 0) +	{ +		const char *p; + +		if (*field && isspace((int)(unsigned char)*field)) +		{ +			++field; +			continue; +		} +		for (p=field; *p; p++) +		{ +			if (*p == ';') +				break; +			if (*p == '=' && header) +				break; +		} + +		if (!header) +		{ +			if ((header=(struct mime_header *) +			     malloc(sizeof(*header))) == 0) +				return NULL; + +			if ((header->header_name=malloc(p-field+1)) == 0) +			{ +				free(header); +				return (NULL); +			} +			memcpy(header->header_name, field, p-field); +			header->header_name[p-field]=0; +			header->attr_list=NULL; +			striptrlspc(header->header_name); +			field=p; +		} +		else +		{ +			struct mime_attribute *a=(struct mime_attribute *) +				malloc(sizeof(struct mime_attribute)); +			int pass, len; +			char *s=0; + +			if (!a) +			{ +				libmail_mimeheader_free(header); +				return NULL; +			} +			if ((a->name=malloc(p-field+1)) == NULL) +			{ +				free(a); +				libmail_mimeheader_free(header); +				return NULL; +			} +			memcpy(a->name, field, p-field); +			a->name[p-field]=0; +			striptrlspc(a->name); +			a->value=0; +			a->next=0; +			if (last_attr) +			{ +				last_attr->next=a; +			} +			else +			{ +				header->attr_list=a; +			} +			last_attr=a; + +			if (*p == '=') +				++p; + +			while (*p && isspace((int)(unsigned char)*p)) +			{ +				++p; +			} + +			field=p; + +			len=0; +			for (pass=0; pass<2; pass++) +			{ +				int quote=0; + +				if (pass) +				{ +					s=a->value=malloc(len); +					if (!s) +					{ +						libmail_mimeheader_free(header); +						return NULL; +					} +				} +				len=0; + +				for (p=field; *p; ) +				{ +					if (*p == ';' && !quote) +					{ +						break; +					} + +					if (*p == '"') +					{ +						quote= !quote; +						++p; +						continue; +					} + +					if (*p == '\\' && p[1]) +						++p; + +					if (pass) +						s[len]= *p; +					++len; +					++p; +				} +				if (pass) +					s[len]=0; +				++len; +			} + +			striptrlspc(a->value); +			field=p; +			if (a->value[0] == 0) +			{ +				free(a->value); +				a->value=0; +			} +		} +		if (*field == ';') +			++field; +	} + +	return (header); +} + +const char *libmail_mimeheader_getattr(struct mime_header *m, const char *name) +{ +	struct mime_attribute *a; + +	for (a=m->attr_list; a; a=a->next) +	{ +		if (strcasecmp(a->name, name) == 0) +			return (a->value); +	} +	return (0); +} + +#if 0 +void libmail_mimeheader_setattr(struct mime_header *m, +				const char *name, const char *value) +{ +	struct mime_attribute *a, *lasta=0; + +	for (a=m->attr_list; a; a=a->next) +	{ +		if (strcasecmp(a->name, name) == 0) +		{ +			if (a->value) +				free(a->value); +			a->value=0; +			if (value) +			{ +				a->value=strdup(value); +				if (!a->value) +				{ +					perror("strdup"); +					exit(1); +				} +			} +			return; +		} +		lasta=a; +	} + +	a=(struct mime_attribute *)malloc(sizeof(struct mime_attribute)); +	if (!a) +	{ +		perror("malloc"); +		exit(1); +	} +	if (!(a->name=strdup(name))) +	{ +		free(a); +		perror("malloc"); +		exit(1); +	} + +	a->value=0; +	a->next=0; +	if (value && !(a->value=strdup(value))) +	{ +		free(a->name); +		free(a); +		perror("malloc"); +		exit(1); +	} + +	if (lasta) +		lasta->next=a; +	else +		m->attr_list=a; +} + +void print_mime_header(FILE *f, struct mime_header *m) +{ +	struct mime_attribute *a; + +	fprintf(f, "%s", m->header_name); + +	for (a=m->attr_list; a; a=a->next) +	{ +		const char *p; + +		fprintf(f, "; %s%s", a->name, a->value ? "=":""); +		if (!a->value) +			continue; + +		for (p=a->value; *p; p++) +			if (strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_", *p) == NULL) +				break; + +		if (!*p) +		{ +			fprintf(f, "%s", a->value); +			continue; +		} +		putc('"', f); +		for (p=a->value; *p; p++) +		{ +			if (*p == '"' || *p == '\\') +				putc('\\', f); +			putc(*p, f); +		} +		putc('"', f); +	} +} +#endif diff --git a/gpglib/mimegpgheader.h b/gpglib/mimegpgheader.h new file mode 100644 index 0000000..c907708 --- /dev/null +++ b/gpglib/mimegpgheader.h @@ -0,0 +1,54 @@ +#ifndef mimegpgheader_h +#define mimegpgheader_h +/* +** Copyright 2001 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> + +#ifdef  __cplusplus +extern "C" { +#endif + +struct header { +	struct header *next; +	char *header; +} ; + +struct read_header_context { +	int continue_header; +	int header_len; +	struct header *first, *last; +} ; + +void libmail_readheader_init(struct read_header_context *); +int libmail_readheader(struct read_header_context *, const char *); +struct header *libmail_readheader_finish(struct read_header_context *); +#define READ_START_OF_LINE(cts) ((cts).continue_header == 0) + +void libmail_header_free(struct header *p); +struct header *libmail_header_find(struct header *p, const char *n); +const char *libmail_header_find_txt(struct header *p, const char *n); + +struct mime_header { +	char *header_name; +	struct mime_attribute *attr_list; +} ; + +struct mime_attribute { +	struct mime_attribute *next; +	char *name, *value; +} ; + +void libmail_mimeheader_free(struct mime_header *); +struct mime_header *libmail_mimeheader_parse(const char *); +const char *libmail_mimeheader_getattr(struct mime_header *, const char *); + +#ifdef  __cplusplus +} ; +#endif + +#endif diff --git a/gpglib/mimegpgstack.c b/gpglib/mimegpgstack.c new file mode 100644 index 0000000..0eb61d9 --- /dev/null +++ b/gpglib/mimegpgstack.c @@ -0,0 +1,72 @@ +/* +** Copyright 2001 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include "mimegpgstack.h" + +int libmail_mimestack_push(struct mimestack **s, const char *b) +{ +	struct mimestack *ss=(struct mimestack *) +		malloc(sizeof(**s)); + +	if (!ss) +		return -1; + +	if ((ss->boundary=strdup(b)) == NULL) +	{ +		free(ss); +		return -1; +	} + +	ss->next= *s; +	*s=ss; +	return 0; +} + +void libmail_mimestack_pop(struct mimestack **p) +{ +	struct mimestack *pp= *p; + +	if (pp) +	{ +		*p=pp->next; +		free(pp->boundary); +		free(pp); +	} +} + +void libmail_mimestack_pop_to(struct mimestack **p, struct mimestack *s) +{ +	while (*p) +	{ +		int last=strcmp( (*p)->boundary, s->boundary) == 0; +		libmail_mimestack_pop(p); +		if (last) +			break; +	} +} + +struct mimestack *libmail_mimestack_search(struct mimestack *p, const char *c) +{ +	int l=strlen(c); + +	while (p) +	{ +		int ll=strlen(p->boundary); + +		if (l >= ll && strncasecmp(p->boundary, c, ll) == 0) +			break; +		p=p->next; +	} +	return (p); +} + + diff --git a/gpglib/mimegpgstack.h b/gpglib/mimegpgstack.h new file mode 100644 index 0000000..a4b2d0b --- /dev/null +++ b/gpglib/mimegpgstack.h @@ -0,0 +1,32 @@ +#ifndef mimegpgstack_h +#define mimegpgstack_h + +/* +** Copyright 2001 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" + +#ifdef  __cplusplus +extern "C" { +#endif + +struct mimestack { +	struct mimestack *next; +	char *boundary; +} ; + +int libmail_mimestack_push(struct mimestack **, const char *); +void libmail_mimestack_pop(struct mimestack **); + +struct mimestack *libmail_mimestack_search(struct mimestack *, const char *); + +void libmail_mimestack_pop_to(struct mimestack **, struct mimestack *); + +#ifdef  __cplusplus +} ; +#endif + +#endif diff --git a/gpglib/options.c b/gpglib/options.c new file mode 100644 index 0000000..e758872 --- /dev/null +++ b/gpglib/options.c @@ -0,0 +1,41 @@ +/* +** Copyright 2004 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include "gpg.h" + +char *libmail_gpg_options(const char *p) +{ +	if (!p || !*p) +		p=getenv("GNUPGHOME"); + +	if (p && *p) +	{ +		char *s=malloc(strlen(p)+sizeof("/options")); + +		if (s) +			return (strcat(strcpy(s, p), "/options")); +	} +	else +	{ +		p=getenv("HOME"); + +		if (p && *p) +		{ +			char *s=malloc(strlen(p)+sizeof("/.gnupg/options")); + +			if (s) +				return (strcat(strcpy(s, p), +					       "/.gnupg/options")); +		} +	} +	return NULL; +} diff --git a/gpglib/rfc2045.c b/gpglib/rfc2045.c new file mode 100644 index 0000000..df598ec --- /dev/null +++ b/gpglib/rfc2045.c @@ -0,0 +1,115 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<signal.h> +#include	<errno.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> + +#include	"gpg.h" +#include	"gpglib.h" + +#include	"rfc2045/rfc2045.h" + +struct rfc2045 *libmail_gpgmime_is_multipart_signed(const struct rfc2045 *q) +{ +	struct rfc2045 *p; + +	if (!q->content_type || strcmp(q->content_type, "multipart/signed")) +		return (0); + +	for (p=q->firstpart; p && p->isdummy; p=p->next) +		; + +	if (p && p->next && p->next->content_type && +	    strcmp(p->next->content_type, "application/pgp-signature") == 0) +		return (p); + +	return (NULL); +} + +struct rfc2045 *libmail_gpgmime_is_multipart_encrypted(const struct rfc2045 *q) +{ +	struct rfc2045 *p; + +	if (!q->content_type || strcmp(q->content_type, "multipart/encrypted")) +		return (0); + +	for (p=q->firstpart; p && p->isdummy; p=p->next) +		; + +	if (p && p->content_type && p->next && p->next->content_type && +	    strcmp(p->content_type, "application/pgp-encrypted") == 0 && +	    strcmp(p->next->content_type, "application/octet-stream") == 0) +		return (p->next); + +	return (NULL); +} + +int libmail_gpgmime_has_mimegpg(const struct rfc2045 *q) +{ +	if (libmail_gpgmime_is_multipart_signed(q) || +	    libmail_gpgmime_is_multipart_encrypted(q)) +		return (1); + +	for (q=q->firstpart; q; q=q->next) +	{ +		if (q->isdummy) +			continue; +		if (libmail_gpgmime_has_mimegpg(q)) +			return (1); +	} +	return (0); +} + +int libmail_gpgmime_is_decoded(const struct rfc2045 *q, int *retcode) +{ +	const char *p; + +	if (!q->content_type || strcasecmp(q->content_type, +					   "multipart/x-mimegpg")) +		return (0); + +	p=rfc2045_getattr(q->content_type_attr, "xpgpstatus"); +	if (!p) +		return (0); + +	*retcode=atoi(p); +	return (1); +} + +struct rfc2045 *libmail_gpgmime_decoded_content(const struct rfc2045 *q) +{ +	for (q=q->firstpart; q; q=q->next) +	{ +		if (q->isdummy) +			continue; + +		return (q->next); +	} +	return (NULL); +} + +struct rfc2045 *libmail_gpgmime_signed_content(const struct rfc2045 *p) +{ +	struct rfc2045 *q; + +	for (q=p->firstpart; q; q=q->next) +	{ +		if (q->isdummy) +			continue; + +		return (q); +	} +	return (NULL); +} + diff --git a/gpglib/sign.c b/gpglib/sign.c new file mode 100644 index 0000000..a92a504 --- /dev/null +++ b/gpglib/sign.c @@ -0,0 +1,109 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include	"config.h" +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<unistd.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/time.h> +#if HAVE_FCNTL_H +#include	<fcntl.h> +#endif +#include	"gpg.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +extern int libmail_gpg_stdin, libmail_gpg_stdout, libmail_gpg_stderr; +extern pid_t libmail_gpg_pid; + + +/* +** Sign a key. +*/ + +static int dosignkey(int (*)(const char *, size_t, void *), +		     const char *cmdstr, +		     void *); + +int libmail_gpg_signkey(const char *gpgdir, const char *signthis, const char *signwith, +		int passphrase_fd, +		int (*dump_func)(const char *, size_t, void *), +		int trust_level, +		void *voidarg) +{ +	char *argvec[12]; +	int rc; +	char passphrase_fd_buf[NUMBUFSIZE]; +	int i; + +	argvec[0]="gpg"; +	argvec[1]="--command-fd"; +	argvec[2]="0"; +	argvec[3]="--default-key"; +	argvec[4]=(char *)signwith; +	argvec[5]="-q"; +	argvec[6]="--no-tty"; + +	i=7; +	if (passphrase_fd >= 0 && fcntl(passphrase_fd, F_SETFD, 0) >= 0) +	{ +		GPGARGV_PASSPHRASE_FD(argvec, i, passphrase_fd, +				      passphrase_fd_buf); +	} + +	argvec[i++]="--sign-key"; +	argvec[i++]=(char *)signthis; +	argvec[i]=0; + +	if (libmail_gpg_fork(&libmail_gpg_stdin, &libmail_gpg_stdout, NULL, gpgdir, argvec) < 0) +		rc= -1; +	else +	{ +		int rc2; + +		char cmdstr[10]; + +#if GPG_HAS_CERT_CHECK_LEVEL + +		cmdstr[0]='0'; + +		if (trust_level > 0 && trust_level <= 9) +			cmdstr[0]='0' + trust_level; + +		strcpy(cmdstr+1, "\nY\n"); + +#else +		strcpy(cmdstr, "Y\n"); +#endif + +		rc=dosignkey(dump_func, cmdstr, voidarg); +		rc2=libmail_gpg_cleanup(); +		if (rc2) +			rc=rc2; +	} +	return (rc); +} + +static int dosignkey(int (*dump_func)(const char *, size_t, void *), +		     const char *cmdstr, +		     void *voidarg) +{ +	int rc=libmail_gpg_write( cmdstr, strlen(cmdstr), +			 dump_func, NULL, NULL, 0, voidarg); +	int rc2; + +	if (rc == 0) +		rc=libmail_gpg_read(dump_func, NULL, NULL, 0, voidarg); +	rc2=libmail_gpg_cleanup(); +	if (rc == 0) +		rc=rc2; +	return (rc); +} diff --git a/gpglib/tempname.c b/gpglib/tempname.c new file mode 100644 index 0000000..30a6faf --- /dev/null +++ b/gpglib/tempname.c @@ -0,0 +1,77 @@ +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif +#include "tempname.h" + +static const char base64[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-,"; + +int libmail_tempfile(char *filename_buf) +{ +	unsigned long seed; +	int i; +	int fd; + +#if HAVE_GETTIMEOFDAY +	struct timeval tv; + +	gettimeofday(&tv, NULL); + +	seed=tv.tv_sec; +	seed ^= tv.tv_usec << 16; +#else +	time_t t; + +	time(&t); +	seed=t; +#endif +	seed ^= getpid(); + +	for (i=0; i<1000; i++, seed += 5000) +	{ +		char *p; +		unsigned long n; + +		strcpy(filename_buf, "/tmp/mimegpg."); + +		p=filename_buf + strlen(filename_buf); + +		n=seed; +		*p++=base64[ n % 64 ]; n /= 64; +		*p++=base64[ n % 64 ]; n /= 64; +		*p++=base64[ n % 64 ]; n /= 64; +		*p++=base64[ n % 64 ]; n /= 64; +		*p++=base64[ n % 64 ]; n /= 64; +		*p++=base64[ n % 64 ]; +		*p=0; + +		if ((fd=open(filename_buf, O_RDWR | O_CREAT | O_EXCL, 0600)) +		    >= 0) +			return (fd); + + +		if (errno != EEXIST) +			break; +	} +	return (-1); +} diff --git a/gpglib/tempname.h b/gpglib/tempname.h new file mode 100644 index 0000000..66b4bd4 --- /dev/null +++ b/gpglib/tempname.h @@ -0,0 +1,23 @@ +#ifndef tempname_h +#define tempname_h +/* +** Copyright 2001-2003 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +#include "config.h" + +#ifdef  __cplusplus +extern "C" { +#endif + +#define TEMPNAMEBUFSIZE	64 + +int libmail_tempfile(char *); + +#ifdef  __cplusplus +} ; +#endif + +#endif diff --git a/gpglib/testgpg.c b/gpglib/testgpg.c new file mode 100644 index 0000000..a08620b --- /dev/null +++ b/gpglib/testgpg.c @@ -0,0 +1,184 @@ +/* +** Copyright 2002-2006 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + + +#include	"config.h" +#include	"gpglib.h" + +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" + +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> + +static int dump_stdout(const char *p, size_t l, void *dummy) +{ +	if (l == 0) +		return 0; + +	if (fwrite(p, l, 1, stdout) != 1) +		exit(1); +	fflush(stdout); +	return (0); +} + +static int poll_wait(void *dummy) +{ +	putchar('*'); +	fflush(stdout); +	return (0); +} + +static void genkey(const char *d) +{ +	libmail_gpg_genkey(d, "iso-8859-1", +		   "John Smith", +		   "john@example.com", +		   "Dummy ISO-8859 Tëëst key", +		   1024, +		   2048, +		   12, +		   'm', +		   "", +		   dump_stdout, +		   poll_wait, +		   NULL); +} + +static int show_key(const char *fingerprint, const char *shortname, +		    const char *key, int invalid, +		    struct gpg_list_info *dummy) +{ +	printf("Fingerprint: %s\nShort: %s\n%s\n\n", +	       fingerprint, +	       shortname, +	       key); +	fflush(stdout); +	return (0); +} + +static void genlist(const char *d, int flag) +{ +	struct gpg_list_info gli; + +	memset(&gli, 0, sizeof(gli)); + +	libmail_gpg_listkeys(d, flag, show_key, dump_stdout, &gli); +} + +static int expkey(const char *d, const char *f, int flag) +{ +	return (libmail_gpg_exportkey(d, flag, f, dump_stdout, dump_stdout, NULL)); +} + +static int delkey(const char *d, const char *f, int flag) +{ +	return (libmail_gpg_deletekey(d, flag, f, dump_stdout, NULL)); +} + +static int signkey(const char *d, const char *signthis, const char *signwith) +{ +	return (libmail_gpg_signkey(d, signthis, signwith, -1, dump_stdout, 0, NULL)); +} + +static int checksign(const char *d, const char *stuff, const char *sig) +{ +	return (libmail_gpg_checksign(d, stuff, sig, dump_stdout, NULL)); +} + +static int import(const char *filename) +{ +	FILE *f=fopen(filename, "r"); +	int rc; + +	if (!f) +	{ +		perror(filename); +		return (1); +	} + +	if ((rc=libmail_gpg_import_start(NULL, 1)) == 0) +	{ +		char buf[BUFSIZ]; +		int n; + +		while ((n=fread(buf, 1, sizeof(buf), f)) > 0) +		{ +			if ((rc=libmail_gpg_import_do(buf, n, +						      dump_stdout, NULL)) != 0) +				return (rc); +		} + +		rc=libmail_gpg_import_finish(dump_stdout, NULL); +	} +	return (rc); +} + +int main(int argc, char **argv) +{ +	if (argc < 3) +		return (0); + +	if (strcmp(argv[2], "gen") == 0) +	{ +		genkey(argv[1]); +		return (0); +	} + +	if (strcmp(argv[2], "listpub") == 0) +	{ +		genlist(argv[1], 0); +		return (0); +	} + +	if (strcmp(argv[2], "listsec") == 0) +	{ +		genlist(argv[1], 1); +		return (0); +	} + +	if (strcmp(argv[2], "exppub") == 0 && argc >= 4) +	{ +		exit(expkey(argv[1], argv[3], 0)); +		return (0); +	} + +	if (strcmp(argv[2], "expsec") == 0 && argc >= 4) +	{ +		exit(expkey(argv[1], argv[3], 1)); +		return (0); +	} + +	if (strcmp(argv[2], "delpub") == 0 && argc >= 4) +	{ +		exit (delkey(argv[1], argv[3], 0)); +		return (0); +	} + +	if (strcmp(argv[2], "delsec") == 0 && argc >= 4) +	{ +		exit (delkey(argv[1], argv[3], 1)); +		return (0); +	} + +	if (strcmp(argv[2], "sign") == 0 && argc >= 5) +	{ +		exit(signkey(argv[1], argv[3], argv[4])); +		return (0); +	} + +	if (strcmp(argv[2], "checksign") == 0 && argc >= 5) +	{ +		exit(checksign(argv[1], argv[3], argv[4])); +		return (0); +	} + +	if (strcmp(argv[2], "import") == 0) +		exit(import(argv[1])); + +	return (0); +} diff --git a/gpglib/webgpg.in b/gpglib/webgpg.in new file mode 100644 index 0000000..c2cc962 --- /dev/null +++ b/gpglib/webgpg.in @@ -0,0 +1,39 @@ +#! @SHELL@ +# +# +# Copyright 2001 Double Precision, Inc.  See COPYING for +# distribution information. +# +# This scripts initializes Maildir/gpg, enabling GPG code for the given +# maildir. + +GPG=@GPG@ + +maildir="$1" + +# Sanity check + +test ! -d "$maildir" && echo "$maildir: not a directory" >&2 && exit 1 + +umask 077 +test -d "$maildir/gpg" && echo "$maildir/gpg: already exists" >&2 && exit 1 +test -f "$maildir/gpg/options" && echo "$maildir/gpg/options: already exists" >&2 && exit 1 + +mkdir $maildir/gpg || exit 1 +cat >$maildir/gpg/options <<EOF + +force-v3-sigs +escape-from-lines +lock-once +comment "" +no-secmem-warning + +EOF + +owner=`@PERL@ -e '@a=stat "'$maildir'"; print "$a[4]:$a[5]";'` + +chown -R $owner $maildir/gpg + +echo "$maildir/gpg/options created, please verify that the options are correct." + +echo "See INSTALL for more information." | 
