diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /gpglib | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
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." |
