From 8265e5d841dba4d70b768475da20372d26f83998 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Tue, 9 Jan 2001 18:27:59 +0000 Subject: Updated. (decode_pgp): Get rid of #ifdef PGP_PROGRAM's and handle 2001-01-09 Jeffrey Stedfast * mail-format.c: Updated. (decode_pgp): Get rid of #ifdef PGP_PROGRAM's and handle appropriately. (handle_multipart_signed): Same. (handle_multipart_encrypted): Same. * Makefile.am: Added openpgp-utils.[c,h] to the build. * openpgp-utils.c: New source file containing all of the pgp interface code. * mail-crypto.c: Removed all of the openpgp funtions as they are being moved to a new file. (mail_crypto_is_rfc2015_signed): Renamed. (mail_crypto_is_rfc2015_encrypted): Renamed. svn path=/trunk/; revision=7320 --- mail/ChangeLog | 18 + mail/Makefile.am | 2 + mail/mail-crypto.c | 990 +------------------------------------------ mail/mail-crypto.h | 36 +- mail/mail-format.c | 26 +- mail/openpgp-utils.c | 1133 ++++++++++++++++++++++++++++++++++++++++++++++++++ mail/openpgp-utils.h | 74 ++++ 7 files changed, 1249 insertions(+), 1030 deletions(-) create mode 100644 mail/openpgp-utils.c create mode 100644 mail/openpgp-utils.h (limited to 'mail') diff --git a/mail/ChangeLog b/mail/ChangeLog index e678e18cb8..08c8294518 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,21 @@ +2001-01-09 Jeffrey Stedfast + + * mail-format.c: Updated. + (decode_pgp): Get rid of #ifdef PGP_PROGRAM's and handle + appropriately. + (handle_multipart_signed): Same. + (handle_multipart_encrypted): Same. + + * Makefile.am: Added openpgp-utils.[c,h] to the build. + + * openpgp-utils.c: New source file containing all of the pgp + interface code. + + * mail-crypto.c: Removed all of the openpgp funtions as they are + being moved to a new file. + (mail_crypto_is_rfc2015_signed): Renamed. + (mail_crypto_is_rfc2015_encrypted): Renamed. + 2001-01-09 Jeffrey Stedfast * session.c (mail_session_set_password): New function to set the diff --git a/mail/Makefile.am b/mail/Makefile.am index e981d817cf..d5c038d8da 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -91,6 +91,8 @@ evolution_mail_SOURCES = \ main.c \ message-list.c \ message-list.h \ + openpgp-utils.c \ + openpgp-utils.h \ session.c \ mail-session.h \ subscribe-dialog.c \ diff --git a/mail/mail-crypto.c b/mail/mail-crypto.c index 95042da6b0..b07231aa00 100644 --- a/mail/mail-crypto.c +++ b/mail/mail-crypto.c @@ -34,7 +34,6 @@ #include -#ifdef PGP_PROGRAM #include #include @@ -59,975 +58,10 @@ #include -static int -cleanup_child (pid_t child) -{ - int status; - pid_t wait_result; - sigset_t mask, omask; - - /* PGP5 closes fds before exiting, meaning this might be called - * too early. So wait a bit for the result. - */ - sigemptyset (&mask); - sigaddset (&mask, SIGALRM); - sigprocmask (SIG_BLOCK, &mask, &omask); - alarm (1); - wait_result = waitpid (child, &status, 0); - alarm (0); - sigprocmask (SIG_SETMASK, &omask, NULL); - - if (wait_result == -1 && errno == EINTR) { - /* The child is hanging: send a friendly reminder. */ - kill (child, SIGTERM); - sleep (1); - wait_result = waitpid (child, &status, WNOHANG); - if (wait_result == 0) { - /* Still hanging; use brute force. */ - kill (child, SIGKILL); - sleep (1); - wait_result = waitpid (child, &status, WNOHANG); - } - } - - if (wait_result != -1 && WIFEXITED (status)) - return WEXITSTATUS (status); - else - return -1; -} - -static void -cleanup_before_exec (int fd) -{ - int maxfd, i; - - maxfd = sysconf (_SC_OPEN_MAX); - if (maxfd < 0) - return; - - /* Loop over all fds. */ - for (i = 0; i < maxfd; i++) { - if ((STDIN_FILENO != i) && - (STDOUT_FILENO != i) && - (STDERR_FILENO != i) && - (fd != i)) - close (i); - } -} - -static int -crypto_exec_with_passwd (const char *path, char *argv[], const char *input, int inlen, - int passwd_fds[], const char *passphrase, - char **output, int *outlen, char **diagnostics) -{ - fd_set fdset, write_fdset; - int ip_fds[2], op_fds[2], diag_fds[2]; - int select_result, read_len, write_len; - size_t tmp_len; - pid_t child; - char *buf, *diag_buf; - const char *passwd_next, *input_next; - size_t size, alloc_size, diag_size, diag_alloc_size; - gboolean eof_seen, diag_eof_seen, passwd_eof_seen, input_eof_seen; - size_t passwd_remaining, passwd_incr, input_remaining, input_incr; - struct timeval timeout; - - - if ((pipe (ip_fds) < 0 ) || - (pipe (op_fds) < 0 ) || - (pipe (diag_fds) < 0 )) { - *diagnostics = g_strdup_printf ("Couldn't create pipe to %s: " - "%s", PGP_PROGRAM, - g_strerror (errno)); - return 0; - } - - if (!(child = fork ())) { - /* In child */ - - if ((dup2 (ip_fds[0], STDIN_FILENO) < 0 ) || - (dup2 (op_fds[1], STDOUT_FILENO) < 0 ) || - (dup2 (diag_fds[1], STDERR_FILENO) < 0 )) { - _exit (255); - } - - /* Dissociate from evolution-mail's controlling - * terminal so that pgp/gpg won't be able to read from - * it: PGP 2 will fall back to asking for the password - * on /dev/tty if the passed-in password is incorrect. - * This will make that fail rather than hanging. - */ - setsid (); - - /* Close excess fds */ - cleanup_before_exec (passwd_fds[0]); - - execvp (path, argv); - fprintf (stderr, "Could not execute %s: %s\n", argv[0], - g_strerror (errno)); - _exit (255); - } else if (child < 0) { - *diagnostics = g_strdup_printf ("Cannot fork %s: %s", - argv[0], g_strerror (errno)); - return 0; - } - - /* Parent */ - close (ip_fds[0]); - close (op_fds[1]); - close (diag_fds[1]); - close (passwd_fds[0]); - - timeout.tv_sec = 10; /* timeout in seconds */ - timeout.tv_usec = 0; - - size = diag_size = 0; - alloc_size = 4096; - diag_alloc_size = 1024; - eof_seen = diag_eof_seen = FALSE; - - buf = g_malloc (alloc_size); - diag_buf = g_malloc (diag_alloc_size); - - passwd_next = passphrase; - passwd_remaining = passphrase ? strlen (passphrase) : 0; - passwd_incr = fpathconf (passwd_fds[1], _PC_PIPE_BUF); - /* Use a reasonable default value on error. */ - if (passwd_incr <= 0) - passwd_incr = 1024; - passwd_eof_seen = FALSE; - - input_next = input; - input_remaining = inlen; - input_incr = fpathconf (ip_fds[1], _PC_PIPE_BUF); - if (input_incr <= 0) - input_incr = 1024; - input_eof_seen = FALSE; - - while (!(eof_seen && diag_eof_seen)) { - FD_ZERO (&fdset); - if (!eof_seen) - FD_SET (op_fds[0], &fdset); - if (!diag_eof_seen) - FD_SET (diag_fds[0], &fdset); - - FD_ZERO (&write_fdset); - if (!passwd_eof_seen) - FD_SET (passwd_fds[1], &write_fdset); - if (!input_eof_seen) - FD_SET (ip_fds[1], &write_fdset); - - select_result = select (FD_SETSIZE, &fdset, &write_fdset, - NULL, &timeout); - if (select_result < 0) { - if (errno == EINTR) - continue; - break; - } - if (select_result == 0) { - /* timeout */ - break; - } - - if (FD_ISSET (op_fds[0], &fdset)) { - /* More output is available. */ - - if (size + 4096 > alloc_size) { - alloc_size += 4096; - buf = g_realloc (buf , alloc_size); - } - read_len = read (op_fds[0], &buf[size], - alloc_size - size - 1); - if (read_len < 0) { - if (errno == EINTR) - continue; - break; - } - if (read_len == 0) - eof_seen = TRUE; - size += read_len; - } - - if (FD_ISSET(diag_fds[0], &fdset) ) { - /* More stderr is available. */ - - if (diag_size + 1024 > diag_alloc_size) { - diag_alloc_size += 1024; - diag_buf = g_realloc (diag_buf, - diag_alloc_size); - } - - read_len = read (diag_fds[0], &diag_buf[diag_size], - diag_alloc_size - diag_size - 1); - if (read_len < 0) { - if (errno == EINTR) - continue; - break; - } - if (read_len == 0) - diag_eof_seen = TRUE; - diag_size += read_len; - } - - if (FD_ISSET(passwd_fds[1], &write_fdset)) { - /* Ready for more password input. */ - - tmp_len = passwd_incr; - if (tmp_len > passwd_remaining) - tmp_len = passwd_remaining; - write_len = write (passwd_fds[1], passwd_next, - tmp_len); - if (write_len < 0) { - if (errno == EINTR) - continue; - break; - } - passwd_next += write_len; - passwd_remaining -= write_len; - if (passwd_remaining == 0) { - close (passwd_fds[1]); - passwd_eof_seen = TRUE; - } - } - - if (FD_ISSET(ip_fds[1], &write_fdset)) { - /* Ready for more ciphertext input. */ - - tmp_len = input_incr; - if (tmp_len > input_remaining) - tmp_len = input_remaining; - write_len = write (ip_fds[1], input_next, tmp_len); - if (write_len < 0) { - if (errno == EINTR) - continue; - break; - } - input_next += write_len; - input_remaining -= write_len; - if (input_remaining == 0 ) { - close (ip_fds[1]); - input_eof_seen = TRUE; - } - } - } - - buf[size] = 0; - diag_buf[diag_size] = 0; - close (op_fds[0]); - close (diag_fds[0]); - - *output = buf; - if (outlen) - *outlen = size; - *diagnostics = diag_buf; - - return cleanup_child (child); -} - -/*----------------------------------------------------------------------* - * Public crypto functions - *----------------------------------------------------------------------*/ - -/** - * mail_crypto_openpgp_decrypt: pgp decrypt ciphertext - * @ciphertext: ciphertext to decrypt - * @outlen: output length of the decrypted data (to be set by #mail_crypto_openpgp_decrypt) - * @ex: a CamelException - * - * Returns an allocated buffer containing the decrypted ciphertext. If - * the cleartext is plain text then you may treat it like a normal - * string as it will be NUL terminated, however #outlen is also set in - * the case that the cleartext is a binary stream. - **/ -char * -mail_crypto_openpgp_decrypt (const char *ciphertext, int cipherlen, - int *outlen, CamelException *ex) -{ - int retval, i; - char *path, *argv[12]; - char *passphrase, *plaintext = NULL, *diagnostics = NULL; - int passwd_fds[2]; - char passwd_fd[32]; - - passphrase = mail_session_request_dialog ( - _("Please enter your PGP/GPG passphrase."), - TRUE, "pgp", FALSE); - if (!passphrase) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("No password provided.")); - return NULL; - } - - if (pipe (passwd_fds) < 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Couldn't create pipe to GPG/PGP: %s"), - g_strerror (errno)); - return NULL; - } - - i = 0; -#if defined(GPG_PATH) - path = GPG_PATH; - - argv[i++] = "gpg"; - argv[i++] = "--verbose"; - argv[i++] = "--yes"; - argv[i++] = "--batch"; - - argv[i++] = "--output"; - argv[i++] = "-"; /* output to stdout */ - - argv[i++] = "--decrypt"; - - argv[i++] = "--passphrase-fd"; - sprintf (passwd_fd, "%d", passwd_fds[0]); - argv[i++] = passwd_fd; -#elif defined(PGP5_PATH) - path = PGP5_PATH; - - argv[i++] = "pgpv"; - argv[i++] = "-f"; - argv[i++] = "+batchmode=1"; - - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); -#else - path = PGP_PATH; - - argv[i++] = "pgp"; - argv[i++] = "-f"; - - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); -#endif - argv[i++] = NULL; - - /* initialize outlen */ - *outlen = 0; - - retval = crypto_exec_with_passwd (path, argv, - ciphertext, cipherlen, - passwd_fds, passphrase, - &plaintext, outlen, - &diagnostics); - - if (retval != 0 || *outlen == 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - "%s", diagnostics); - g_free (plaintext); - g_free (diagnostics); - return NULL; - } - - g_free (diagnostics); - - return plaintext; -} - -/** - * mail_crypto_openpgp_encrypt: pgp encrypt data - * @in: data to encrypt - * @inlen: input length of the input data (which may be a binary stream) - * @recipients: an array of recipients to encrypt to (preferably each - * element should be a pgp keyring ID however sometimes email - * addresses will work assuming that your pgp keyring has an - * entry for that address) - * @sign: TRUE if you wish to sign the encrypted text as well, FALSE otherwise - * @userid: userid to sign with assuming #sign is TRUE - * @ex: a CamelException - * - * Encrypts the plaintext to the list of recipients and optionally signs - **/ -char * -mail_crypto_openpgp_encrypt (const char *in, int inlen, - const GPtrArray *recipients, - gboolean sign, const char *userid, - CamelException *ex) -{ - GPtrArray *recipient_list = NULL; - GPtrArray *argv; - int retval, r; - char *path; - char *passphrase = NULL, *ciphertext = NULL, *diagnostics = NULL; - int passwd_fds[2]; - char passwd_fd[32]; - - if (sign) { - /* we only need the passphrase if we plan to sign */ - passphrase = mail_session_request_dialog ( - _("Please enter your PGP/GPG passphrase."), - TRUE, "pgp", FALSE); - if (!passphrase) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("No password provided.")); - return NULL; - } - } - - if (pipe (passwd_fds) < 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Couldn't create pipe to GPG/PGP: %s"), - g_strerror (errno)); - return NULL; - } - - argv = g_ptr_array_new (); -#if defined(GPG_PATH) - path = GPG_PATH; - - recipient_list = g_ptr_array_new (); - for (r = 0; r < recipients->len; r++) { - char *buf, *recipient; - - recipient = recipients->pdata[r]; - buf = g_strdup_printf ("-r %s", recipient); - g_ptr_array_add (recipient_list, buf); - } - - g_ptr_array_add (argv, "gpg"); - g_ptr_array_add (argv, "--verbose"); - g_ptr_array_add (argv, "--yes"); - g_ptr_array_add (argv, "--batch"); - - g_ptr_array_add (argv, "--armor"); - - for (r = 0; r < recipient_list->len; r++) - g_ptr_array_add (argv, recipient_list->pdata[r]); - - g_ptr_array_add (argv, "--output"); - g_ptr_array_add (argv, "-"); /* output to stdout */ - - g_ptr_array_add (argv, "--encrypt"); - - if (sign) { - g_ptr_array_add (argv, "--sign"); - - g_ptr_array_add (argv, "-u"); - g_ptr_array_add (argv, (gchar *) userid); - - g_ptr_array_add (argv, "--passphrase-fd"); - sprintf (passwd_fd, "%d", passwd_fds[0]); - g_ptr_array_add (argv, passwd_fd); - } -#elif defined(PGP5_PATH) - path = PGP5_PATH; - - recipient_list = g_ptr_array_new (); - for (r = 0; r < recipients->len; r++) { - char *buf, *recipient; - - recipient = recipients->pdata[r]; - buf = g_strdup_printf ("-r %s", recipient); - g_ptr_array_add (recipient_list, buf); - } - - g_ptr_array_add (argv, "pgpe"); - - for (r = 0; r < recipient_list->len; r++) - g_ptr_array_add (argv, recipient_list->pdata[r]); - - g_ptr_array_add (argv, "-f"); - g_ptr_array_add (argv, "-z"); - g_ptr_array_add (argv, "-a"); - g_ptr_array_add (argv, "-o"); - g_ptr_array_add (argv, "-"); /* output to stdout */ - - if (sign) { - g_ptr_array_add (argv, "-s"); - - g_ptr_array_add (argv, "-u"); - g_ptr_array_add (argv, (gchar *) userid); - - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); - } -#else - path = PGP_PATH; - - recipient_list = g_ptr_array_new (); - for (r = 0; r < recipients->len; r++) { - char *buf, *recipient; - - recipient = recipients->pdata[r]; - buf = g_strdup_printf ("-r %s", recipient); - g_ptr_array_add (recipient_list, buf); - } - - g_ptr_array_add (argv, "pgp"); - g_ptr_array_add (argv, "-f"); - g_ptr_array_add (argv, "-e"); - g_ptr_array_add (argv, "-a"); - g_ptr_array_add (argv, "-o"); - g_ptr_array_add (argv, "-"); - - for (r = 0; r < recipient_list->len; r++) - g_ptr_array_add (argv, recipient_list->pdata[r]); - - if (sign) { - g_ptr_array_add (argv, "-s"); - - g_ptr_array_add (argv, "-u"); - g_ptr_array_add (argv, (gchar *) userid); - - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); - } -#endif - g_ptr_array_add (argv, NULL); - - retval = crypto_exec_with_passwd (path, (char **) argv->pdata, - in, inlen, passwd_fds, - passphrase, &ciphertext, NULL, - &diagnostics); - - if (retval != 0 || !*ciphertext) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - "%s", diagnostics); - g_free (ciphertext); - ciphertext = NULL; - } - - if (recipient_list) { - for (r = 0; r < recipient_list->len; r++) - g_free (recipient_list->pdata[r]); - g_ptr_array_free (recipient_list, TRUE); - } - - g_ptr_array_free (argv, TRUE); - - g_free (diagnostics); - - return ciphertext; -} - -/** - * mail_crypto_openpgp_clearsign: pgp clearsign plaintext - * @plaintext: text to sign - * @userid: user id to sign with - * @hash: Preferred hash function (md5 or sha1) - * @ex: a CamelException - * - * Clearsigns the plaintext using the user id - **/ - -char * -mail_crypto_openpgp_clearsign (const char *plaintext, - const char *userid, - PgpHashType hash, - CamelException *ex) -{ - int retval; - char *path, *argv[20]; - int i; - char *passphrase; - char *ciphertext = NULL; - char *diagnostics = NULL; - int passwd_fds[2]; - char passwd_fd[32]; - char *hash_str = NULL; - -#ifndef PGP_PROGRAM - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("No GPG/PGP program available.")); - return NULL; -#endif - - passphrase = mail_session_request_dialog (_("Please enter your PGP/GPG passphrase."), - TRUE, "pgp", FALSE); - - if (!passphrase) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("No password provided.")); - return NULL; - } - - if (pipe (passwd_fds) < 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Couldn't create pipe to GPG/PGP: %s"), - g_strerror (errno)); - return NULL; - } - - switch (hash) { - case PGP_HASH_TYPE_MD5: - hash_str = "MD5"; - break; - case PGP_HASH_TYPE_SHA1: - hash_str = "SHA1"; - break; - default: - hash_str = NULL; - } - - i = 0; -#if defined(GPG_PATH) - path = GPG_PATH; - - argv[i++] = "gpg"; - - argv[i++] = "--clearsign"; - - if (hash_str) { - argv[i++] = "--digest-algo"; - argv[i++] = hash_str; - } - - if (userid) { - argv[i++] = "-u"; - argv[i++] = (char *) userid; - } - - argv[i++] = "--verbose"; - argv[i++] = "--yes"; - argv[i++] = "--batch"; - - argv[i++] = "--armor"; - - argv[i++] = "--output"; - argv[i++] = "-"; /* output to stdout */ - - argv[i++] = "--passphrase-fd"; - sprintf (passwd_fd, "%d", passwd_fds[0]); - argv[i++] = passwd_fd; -#elif defined(PGP5_PATH) - /* FIXME: modify to respect hash */ - path = PGP5_PATH; - - argv[i++] = "pgps"; - - if (userid) { - argv[i++] = "-u"; - argv[i++] = (char *) userid; - } - - argv[i++] = "-f"; - argv[i++] = "-z"; - argv[i++] = "-a"; - argv[i++] = "-o"; - argv[i++] = "-"; /* output to stdout */ - - argv[i++] = "-s"; - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); -#else - /* FIXME: modify to respect hash */ - path = PGP_PATH; - - argv[i++] = "pgp"; - - argv[i++] = "-f"; - argv[i++] = "-e"; - argv[i++] = "-a"; - argv[i++] = "-o"; - argv[i++] = "-"; - - argv[i++] = "-s"; - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); -#endif - argv[i++] = NULL; - - retval = crypto_exec_with_passwd (path, argv, - plaintext, strlen (plaintext), - passwd_fds, passphrase, - &ciphertext, NULL, - &diagnostics); - - if (retval != 0 || !*ciphertext) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - "%s", diagnostics); - g_free (ciphertext); - ciphertext = NULL; - } - - g_free (diagnostics); - - return ciphertext; -} - - -/** - * mail_crypto_openpgp_sign: - * @in: input data to sign - * @inlen: length of input data - * @userid: userid to sign with - * @hash: preferred hash type (md5 or sha1) - * @ex: exception - * - * Returns an allocated string containing the detached signature using - * the preferred hash. - **/ -char * -mail_crypto_openpgp_sign (const char *in, int inlen, const char *userid, - PgpHashType hash, CamelException *ex) -{ - char *argv[20]; - char *cyphertext = NULL; - char *diagnostics = NULL; - char *passphrase = NULL; - char *hash_str = NULL; - char *path; - int passwd_fds[2]; - char passwd_fd[32]; - int retval, i; - - -#ifndef PGP_PROGRAM - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("No GPG/PGP program available.")); - return NULL; -#endif - - passphrase = mail_session_request_dialog (_("Please enter your PGP/GPG passphrase."), - TRUE, "pgp", FALSE); - if (!passphrase) { - camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, - _("No password provided.")); - return NULL; - } - - if (pipe (passwd_fds) < 0) { - g_free (passphrase); - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Couldn't create pipe to GPG/PGP: %s"), - g_strerror (errno)); - return NULL; - } - - switch (hash) { - case PGP_HASH_TYPE_MD5: - hash_str = "MD5"; - break; - case PGP_HASH_TYPE_SHA1: - hash_str = "SHA1"; - break; - default: - hash_str = NULL; - } - - i = 0; -#if defined(GPG_PATH) - path = GPG_PATH; - argv[i++] = "gpg"; - - argv[i++] = "--sign"; - argv[i++] = "-b"; - if (hash_str) { - argv[i++] = "--digest-algo"; - argv[i++] = hash_str; - } - - if (userid) { - argv[i++] = "-u"; - argv[i++] = (char *) userid; - } - - argv[i++] = "--verbose"; - argv[i++] = "--yes"; - argv[i++] = "--batch"; - - argv[i++] = "--armor"; - - argv[i++] = "--output"; - argv[i++] = "-"; /* output to stdout */ - - argv[i++] = "--passphrase-fd"; - sprintf (passwd_fd, "%d", passwd_fds[0]); - argv[i++] = passwd_fd; -#elif defined(PGP5_PATH) - path = PGP5_PATH; - - /* FIXME: respect hash */ - argv[i++] = "pgps"; - - if (userid) { - argv[i++] = "-u"; - argv[i++] = (char *) userid; - } - - argv[i++] = "-b"; - argv[i++] = "-f"; - argv[i++] = "-z"; - argv[i++] = "-a"; - argv[i++] = "-o"; - argv[i++] = "-"; /* output to stdout */ - - argv[i++] = "-s"; - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); -#else - path = PGP_PATH; - - /* FIXME: needs to respect hash and also return only the detached sig */ - argv[i++] = "pgp"; - - if (userid) { - argv[i++] = "-u"; - argv[i++] = (char *) userid; - } - - argv[i++] = "-f"; - argv[i++] = "-a"; - argv[i++] = "-o"; - argv[i++] = "-"; - - argv[i++] = "-s"; - sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); - putenv (passwd_fd); -#endif - - argv[i++] = NULL; - - retval = crypto_exec_with_passwd (path, argv, - in, inlen, - passwd_fds, passphrase, - &cyphertext, NULL, - &diagnostics); - - g_free (passphrase); - - if (retval != 0 || !*cyphertext) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - "%s", diagnostics); - g_free (cyphertext); - cyphertext = NULL; - } - - g_free (diagnostics); - - return cyphertext; -} - -static char * -swrite (const char *data, int len) -{ - char *template; - int fd; - - template = g_strdup ("/tmp/mail-crypto-XXXXXX"); - fd = mkstemp (template); - if (fd == -1) { - g_free (template); - return NULL; - } - - write (fd, data, len); - close (fd); - - return template; -} - -gboolean -mail_crypto_openpgp_verify (const char *in, int inlen, const char *sigin, int siglen, CamelException *ex) -{ - char *argv[20]; - char *cleartext = NULL; - char *diagnostics = NULL; - char *path; - int passwd_fds[2]; - char passwd_fd[32]; - char *sigfile; - int retval, i, clearlen; - gboolean valid = TRUE; - - -#ifndef PGP_PROGRAM - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("No GPG/PGP program available.")); - return FALSE; -#endif - - if (pipe (passwd_fds) < 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Couldn't create pipe to GPG/PGP: %s"), - g_strerror (errno)); - return FALSE; - } - - if (sigin != NULL && siglen) { - /* We are going to verify a detached signature so save - the signature to a temp file. */ - sigfile = swrite (sigin, siglen); - if (!sigfile) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Couldn't create temp file: %s"), - g_strerror (errno)); - return FALSE; - } - } - - i = 0; -#if defined(GPG_PATH) - path = GPG_PATH; - - argv[i++] = "gpg"; - - argv[i++] = "--verify"; - - if (sigin != NULL && siglen) - argv[i++] = sigfile; - - /* We are going to verify using stdin */ - argv[i++] = "-"; - - /*argv[i++] = "--verbose"; - argv[i++] = "--yes"; - argv[i++] = "--batch";*/ -#elif defined (PGP5_PATH) - path = PGP5_PATH; - - argv[i++] = "pgpv"; - - argv[i++] = "-z"; - - if (sigin != NULL && siglen) - argv[i++] = sigfile; - - argv[i++] = "-f"; - -#else - path = PGP_PATH; - - argv[i++] = "pgp"; - - if (sigin != NULL && siglen) - argv[i++] = sigfile; - - argv[i++] = "-f"; -#endif - - argv[i++] = NULL; - - clearlen = 0; - retval = crypto_exec_with_passwd (path, argv, - in, inlen, - passwd_fds, NULL, - &cleartext, &clearlen, - &diagnostics); - - /* cleanup */ - if (sigfile) { - unlink (sigfile); - g_free (sigfile); - } - - /* FIXME: maybe we should always set an exception? */ - if (retval != 0) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - "%s", diagnostics); - valid = FALSE; - } - - g_free (diagnostics); - g_free (cleartext); - - return valid; -} - /** rfc2015 stuff *******************************/ gboolean -is_rfc2015_signed (CamelMimePart *mime_part) +mail_crypto_is_rfc2015_signed (CamelMimePart *mime_part) { CamelDataWrapper *wrapper; CamelMultipart *mp; @@ -1072,7 +106,7 @@ is_rfc2015_signed (CamelMimePart *mime_part) } gboolean -is_rfc2015_encrypted (CamelMimePart *mime_part) +mail_crypto_is_rfc2015_encrypted (CamelMimePart *mime_part) { CamelDataWrapper *wrapper; CamelMultipart *mp; @@ -1167,7 +201,7 @@ pgp_mime_part_sign (CamelMimePart **mime_part, const gchar *userid, PgpHashType clearlen = array->len; /* get the signature */ - signature = mail_crypto_openpgp_sign (cleartext, clearlen, userid, hash, ex); + signature = openpgp_sign (cleartext, clearlen, userid, hash, ex); g_byte_array_free (array, TRUE); if (camel_exception_is_set (ex)) { /* restore the original encoding */ @@ -1241,7 +275,7 @@ pgp_mime_part_verify (CamelMimePart *mime_part, CamelException *ex) g_return_val_if_fail (mime_part != NULL, FALSE); - if (!is_rfc2015_signed (mime_part)) + if (!mail_crypto_is_rfc2015_signed (mime_part)) return FALSE; wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); @@ -1267,8 +301,8 @@ pgp_mime_part_verify (CamelMimePart *mime_part, CamelException *ex) camel_object_unref (CAMEL_OBJECT (stream)); /* verify */ - valid = mail_crypto_openpgp_verify (content->data, content->len, - sig->data, sig->len, ex); + valid = openpgp_verify (content->data, content->len, + sig->data, sig->len, ex); g_byte_array_free (content, TRUE); g_byte_array_free (sig, TRUE); @@ -1309,9 +343,9 @@ pgp_mime_part_encrypt (CamelMimePart **mime_part, const GPtrArray *recipients, C camel_object_unref (CAMEL_OBJECT (stream)); /* pgp encrypt */ - ciphertext = mail_crypto_openpgp_encrypt (contents->data, - contents->len, - recipients, FALSE, NULL, ex); + ciphertext = openpgp_encrypt (contents->data, + contents->len, + recipients, FALSE, NULL, ex); if (camel_exception_is_set (ex)) return; @@ -1376,7 +410,7 @@ pgp_mime_part_decrypt (CamelMimePart *mime_part, CamelException *ex) g_return_val_if_fail (mime_part != NULL, NULL); /* make sure the mime part is a multipart/encrypted */ - if (!is_rfc2015_encrypted (mime_part)) + if (!mail_crypto_is_rfc2015_encrypted (mime_part)) return NULL; wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); @@ -1397,7 +431,7 @@ pgp_mime_part_decrypt (CamelMimePart *mime_part, CamelException *ex) cipherlen = content->len; /* get the cleartext */ - cleartext = mail_crypto_openpgp_decrypt (ciphertext, cipherlen, &clearlen, ex); + cleartext = openpgp_decrypt (ciphertext, cipherlen, &clearlen, ex); g_byte_array_free (content, TRUE); if (camel_exception_is_set (ex)) return NULL; @@ -1418,5 +452,3 @@ pgp_mime_part_decrypt (CamelMimePart *mime_part, CamelException *ex) return part; } - -#endif /* PGP_PROGRAM */ diff --git a/mail/mail-crypto.h b/mail/mail-crypto.h index 250f46c022..189679257f 100644 --- a/mail/mail-crypto.h +++ b/mail/mail-crypto.h @@ -25,46 +25,16 @@ #include #include +#include "openpgp-utils.h" #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus }*/ -typedef enum { - PGP_HASH_TYPE_NONE, - PGP_HASH_TYPE_MD5, - PGP_HASH_TYPE_SHA1 -} PgpHashType; +gboolean mail_crypto_is_rfc2015_signed (CamelMimePart *part); -char *mail_crypto_openpgp_decrypt (const char *ciphertext, - int cipherlen, - int *outlen, - CamelException *ex); - -char *mail_crypto_openpgp_encrypt (const char *in, int inlen, - const GPtrArray *recipients, - gboolean sign, - const char *userid, - CamelException *ex); - -char *mail_crypto_openpgp_clearsign (const char *plaintext, - const char *userid, - PgpHashType hash, - CamelException *ex); - -char *mail_crypto_openpgp_sign (const char *in, int inlen, - const char *userid, - PgpHashType hash, - CamelException *ex); - -gboolean mail_crypto_openpgp_verify (const char *in, int inlen, - const char *sigin, int siglen, - CamelException *ex); - -gboolean is_rfc2015_signed (CamelMimePart *part); - -gboolean is_rfc2015_encrypted (CamelMimePart *part); +gboolean mail_crypto_is_rfc2015_encrypted (CamelMimePart *part); void pgp_mime_part_sign (CamelMimePart **mime_part, const gchar *userid, diff --git a/mail/mail-format.c b/mail/mail-format.c index 1bfe856ba0..2358c4c352 100644 --- a/mail/mail-format.c +++ b/mail/mail-format.c @@ -838,20 +838,15 @@ decode_pgp (const char *ciphertext, int *outlen, MailDisplay *md) char *plaintext; camel_exception_init (&ex); -#ifdef PGP_PROGRAM + /* FIXME: multipart parts */ /* another FIXME: this doesn't have to return plaintext you realize... */ if (g_datalist_get_data (md->data, "show_pgp")) { - plaintext = mail_crypto_openpgp_decrypt (ciphertext, strlen (ciphertext), outlen, &ex); + plaintext = openpgp_decrypt (ciphertext, strlen (ciphertext), outlen, &ex); if (plaintext) return plaintext; } -#else - camel_exception_set (&ex, CAMEL_EXCEPTION_SYSTEM, - _("No GPG/PGP support available in this copy " - "of Evolution.")); -#endif - + mail_html_write (md->html, md->stream, "
" "" @@ -1199,7 +1194,6 @@ static gboolean handle_multipart_encrypted (CamelMimePart *part, const char *mime_type, MailDisplay *md) { -#ifdef PGP_PROGRAM CamelDataWrapper *wrapper; CamelMimePart *mime_part; CamelException ex; @@ -1208,7 +1202,7 @@ handle_multipart_encrypted (CamelMimePart *part, const char *mime_type, g_return_val_if_fail (CAMEL_IS_MULTIPART (wrapper), FALSE); /* Currently we only handle RFC2015-style PGP encryption. */ - if (!is_rfc2015_encrypted (part)) + if (!mail_crypto_is_rfc2015_encrypted (part)) return handle_multipart_mixed (part, mime_type, md); camel_exception_init (&ex); @@ -1224,9 +1218,6 @@ handle_multipart_encrypted (CamelMimePart *part, const char *mime_type, return retcode; } -#else - return handle_multipart_mixed (part, mime_type, md); -#endif } /* FIXME: So this function is mostly just a place-holder for now */ @@ -1234,7 +1225,6 @@ static gboolean handle_multipart_signed (CamelMimePart *part, const char *mime_type, MailDisplay *md) { -#ifdef PGP_PROGRAM CamelDataWrapper *wrapper; CamelException ex; gboolean valid; @@ -1243,16 +1233,16 @@ handle_multipart_signed (CamelMimePart *part, const char *mime_type, g_return_val_if_fail (CAMEL_IS_MULTIPART (wrapper), FALSE); /* Currently we only handle RFC2015-style PGP signatures. */ - if (!is_rfc2015_signed (part)) + if (!mail_crypto_is_rfc2015_signed (part)) return handle_multipart_mixed (part, mime_type, md); camel_exception_init (&ex); valid = pgp_mime_part_verify (part, &ex); + if (camel_exception_is_set (&ex)) + handle_multipart_mixed (part, mime_type, md); + return valid; -#else - return handle_multipart_mixed (part, mime_type, md); -#endif } /* As seen in RFC 2387! */ diff --git a/mail/openpgp-utils.c b/mail/openpgp-utils.c new file mode 100644 index 0000000000..1240a68188 --- /dev/null +++ b/mail/openpgp-utils.c @@ -0,0 +1,1133 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Nathan Thompson-Amato + * Dan Winship + * Jeffrey Stedfast + * + * Copyright (C) Helix Code, Inc. (www.helixcode.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include /* for _() macro */ +#include "openpgp-utils.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define d(x) + +static const gchar *pgp_path = NULL; +static PgpType pgp_type = PGP_TYPE_NONE; +static PgpPasswdFunc pgp_passwd_func = NULL; +static gpointer pgp_data = NULL; + + +static gchar * +pgp_get_passphrase (const gchar *userid) +{ + gchar *passphrase, *prompt, *type; + + switch (pgp_type) { + case PGP_TYPE_GPG: + type = "GnuPG"; + break; + case PGP_TYPE_PGP5: + type = "PGP5"; + break; + case PGP_TYPE_PGP2: + type = "PGP2.x"; + break; + default: + type = ""; + } + prompt = g_strdup_printf (_("Please enter your %s passphrase%s%s"), + type, userid ? _(" for ") : "", + userid ? userid : ""); + + passphrase = pgp_passwd_func (prompt, pgp_data); + g_free (prompt); + + return passphrase; +} + + +/** + * openpgp_init: + * @path: path to pgp + * @type: pgp program type + * @callback: function to query for a passphrase + * @data: user data + * + * Initializes pgp variables + **/ +void +openpgp_init (const gchar *path, PgpType type, PgpPasswdFunc callback, gpointer data) +{ + pgp_path = path; + pgp_type = type; + pgp_passwd_func = callback; + pgp_data = data; +} + + +/** + * openpgp_detect: + * @text: input text + * + * Returns TRUE if it is found that the text contains a PGP encrypted + * block otherwise returns FALSE. + **/ +gboolean +openpgp_detect (const gchar *text) +{ + if (strstr (text, "-----BEGIN PGP MESSAGE-----")) + return TRUE; + return FALSE; +} + + +/** + * openpgp_sign_detect: + * @text: input text + * + * Returns TRUE if it is found that the text contains a PGP signed + * block otherwise returns FALSE. + **/ +gboolean +openpgp_sign_detect (const gchar *text) +{ + if (strstr (text, "-----BEGIN PGP SIGNED MESSAGE-----")) + return TRUE; + return FALSE; +} + +static int +cleanup_child (pid_t child) +{ + int status; + pid_t wait_result; + sigset_t mask, omask; + + /* PGP5 closes fds before exiting, meaning this might be called + * too early. So wait a bit for the result. + */ + sigemptyset (&mask); + sigaddset (&mask, SIGALRM); + sigprocmask (SIG_BLOCK, &mask, &omask); + alarm (1); + wait_result = waitpid (child, &status, 0); + alarm (0); + sigprocmask (SIG_SETMASK, &omask, NULL); + + if (wait_result == -1 && errno == EINTR) { + /* The child is hanging: send a friendly reminder. */ + kill (child, SIGTERM); + sleep (1); + wait_result = waitpid (child, &status, WNOHANG); + if (wait_result == 0) { + /* Still hanging; use brute force. */ + kill (child, SIGKILL); + sleep (1); + wait_result = waitpid (child, &status, WNOHANG); + } + } + + if (wait_result != -1 && WIFEXITED (status)) + return WEXITSTATUS (status); + else + return -1; +} + +static void +cleanup_before_exec (int fd) +{ + int maxfd, i; + + maxfd = sysconf (_SC_OPEN_MAX); + if (maxfd < 0) + return; + + /* Loop over all fds. */ + for (i = 0; i < maxfd; i++) { + if ((STDIN_FILENO != i) && + (STDOUT_FILENO != i) && + (STDERR_FILENO != i) && + (fd != i)) + close (i); + } +} + +static int +crypto_exec_with_passwd (const char *path, char *argv[], const char *input, int inlen, + int passwd_fds[], const char *passphrase, + char **output, int *outlen, char **diagnostics) +{ + fd_set fdset, write_fdset; + int ip_fds[2], op_fds[2], diag_fds[2]; + int select_result, read_len, write_len; + size_t tmp_len; + pid_t child; + char *buf, *diag_buf; + const char *passwd_next, *input_next; + size_t size, alloc_size, diag_size, diag_alloc_size; + gboolean eof_seen, diag_eof_seen, passwd_eof_seen, input_eof_seen; + size_t passwd_remaining, passwd_incr, input_remaining, input_incr; + struct timeval timeout; + + + if ((pipe (ip_fds) < 0 ) || + (pipe (op_fds) < 0 ) || + (pipe (diag_fds) < 0 )) { + *diagnostics = g_strdup_printf ("Couldn't create pipe to %s: " + "%s", pgp_path, + g_strerror (errno)); + return 0; + } + + if (!(child = fork ())) { + /* In child */ + + if ((dup2 (ip_fds[0], STDIN_FILENO) < 0 ) || + (dup2 (op_fds[1], STDOUT_FILENO) < 0 ) || + (dup2 (diag_fds[1], STDERR_FILENO) < 0 )) { + _exit (255); + } + + /* Dissociate from evolution-mail's controlling + * terminal so that pgp/gpg won't be able to read from + * it: PGP 2 will fall back to asking for the password + * on /dev/tty if the passed-in password is incorrect. + * This will make that fail rather than hanging. + */ + setsid (); + + /* Close excess fds */ + cleanup_before_exec (passwd_fds[0]); + + execvp (path, argv); + fprintf (stderr, "Could not execute %s: %s\n", argv[0], + g_strerror (errno)); + _exit (255); + } else if (child < 0) { + *diagnostics = g_strdup_printf ("Cannot fork %s: %s", + argv[0], g_strerror (errno)); + return 0; + } + + /* Parent */ + close (ip_fds[0]); + close (op_fds[1]); + close (diag_fds[1]); + close (passwd_fds[0]); + + timeout.tv_sec = 10; /* timeout in seconds */ + timeout.tv_usec = 0; + + size = diag_size = 0; + alloc_size = 4096; + diag_alloc_size = 1024; + eof_seen = diag_eof_seen = FALSE; + + buf = g_malloc (alloc_size); + diag_buf = g_malloc (diag_alloc_size); + + passwd_next = passphrase; + passwd_remaining = passphrase ? strlen (passphrase) : 0; + passwd_incr = fpathconf (passwd_fds[1], _PC_PIPE_BUF); + /* Use a reasonable default value on error. */ + if (passwd_incr <= 0) + passwd_incr = 1024; + passwd_eof_seen = FALSE; + + input_next = input; + input_remaining = inlen; + input_incr = fpathconf (ip_fds[1], _PC_PIPE_BUF); + if (input_incr <= 0) + input_incr = 1024; + input_eof_seen = FALSE; + + while (!(eof_seen && diag_eof_seen)) { + FD_ZERO (&fdset); + if (!eof_seen) + FD_SET (op_fds[0], &fdset); + if (!diag_eof_seen) + FD_SET (diag_fds[0], &fdset); + + FD_ZERO (&write_fdset); + if (!passwd_eof_seen) + FD_SET (passwd_fds[1], &write_fdset); + if (!input_eof_seen) + FD_SET (ip_fds[1], &write_fdset); + + select_result = select (FD_SETSIZE, &fdset, &write_fdset, + NULL, &timeout); + if (select_result < 0) { + if (errno == EINTR) + continue; + break; + } + if (select_result == 0) { + /* timeout */ + break; + } + + if (FD_ISSET (op_fds[0], &fdset)) { + /* More output is available. */ + + if (size + 4096 > alloc_size) { + alloc_size += 4096; + buf = g_realloc (buf , alloc_size); + } + read_len = read (op_fds[0], &buf[size], + alloc_size - size - 1); + if (read_len < 0) { + if (errno == EINTR) + continue; + break; + } + if (read_len == 0) + eof_seen = TRUE; + size += read_len; + } + + if (FD_ISSET(diag_fds[0], &fdset) ) { + /* More stderr is available. */ + + if (diag_size + 1024 > diag_alloc_size) { + diag_alloc_size += 1024; + diag_buf = g_realloc (diag_buf, + diag_alloc_size); + } + + read_len = read (diag_fds[0], &diag_buf[diag_size], + diag_alloc_size - diag_size - 1); + if (read_len < 0) { + if (errno == EINTR) + continue; + break; + } + if (read_len == 0) + diag_eof_seen = TRUE; + diag_size += read_len; + } + + if (FD_ISSET(passwd_fds[1], &write_fdset)) { + /* Ready for more password input. */ + + tmp_len = passwd_incr; + if (tmp_len > passwd_remaining) + tmp_len = passwd_remaining; + write_len = write (passwd_fds[1], passwd_next, + tmp_len); + if (write_len < 0) { + if (errno == EINTR) + continue; + break; + } + passwd_next += write_len; + passwd_remaining -= write_len; + if (passwd_remaining == 0) { + close (passwd_fds[1]); + passwd_eof_seen = TRUE; + } + } + + if (FD_ISSET(ip_fds[1], &write_fdset)) { + /* Ready for more ciphertext input. */ + + tmp_len = input_incr; + if (tmp_len > input_remaining) + tmp_len = input_remaining; + write_len = write (ip_fds[1], input_next, tmp_len); + if (write_len < 0) { + if (errno == EINTR) + continue; + break; + } + input_next += write_len; + input_remaining -= write_len; + if (input_remaining == 0 ) { + close (ip_fds[1]); + input_eof_seen = TRUE; + } + } + } + + buf[size] = 0; + diag_buf[diag_size] = 0; + close (op_fds[0]); + close (diag_fds[0]); + + *output = buf; + if (outlen) + *outlen = size; + *diagnostics = diag_buf; + + return cleanup_child (child); +} + +/*----------------------------------------------------------------------* + * Public crypto functions + *----------------------------------------------------------------------*/ + +/** + * openpgp_decrypt: + * @ciphertext: ciphertext to decrypt + * @cipherlen: ciphertext length + * @outlen: output length of the decrypted data (to be set by #openpgp_decrypt) + * @ex: exception + * + * Returns an allocated buffer containing the decrypted ciphertext. If + * the cleartext is plain text then you may treat it like a normal + * string as it will be NUL terminated, however #outlen is also set in + * the case that the cleartext is a binary stream. + **/ +gchar * +openpgp_decrypt (const gchar *ciphertext, gint cipherlen, gint *outlen, CamelException *ex) +{ + char *argv[15]; + char *plaintext = NULL; + char *diagnostics = NULL; + char *passphrase; + int passwd_fds[2]; + char passwd_fd[32]; + int retval, i; + + if (pgp_type == PGP_TYPE_NONE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return NULL; + } + + passphrase = pgp_get_passphrase (NULL); + if (!passphrase) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No password provided.")); + return NULL; + } + + if (pipe (passwd_fds) < 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return NULL; + } + + i = 0; + switch (pgp_type) { + case PGP_TYPE_GPG: + argv[i++] = "gpg"; + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "--decrypt"; + + argv[i++] = "--passphrase-fd"; + sprintf (passwd_fd, "%d", passwd_fds[0]); + argv[i++] = passwd_fd; + break; + case PGP_TYPE_PGP5: + argv[i++] = "pgpv"; + argv[i++] = "-f"; + argv[i++] = "+batchmode=1"; + + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + break; + case PGP_TYPE_PGP2: + argv[i++] = "pgp"; + argv[i++] = "-f"; + + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + break; + default: + break; + } + + argv[i++] = NULL; + + retval = crypto_exec_with_passwd (pgp_path, argv, + ciphertext, cipherlen, + passwd_fds, passphrase, + &plaintext, outlen, + &diagnostics); + g_free (passphrase); + + if (retval != 0 || !*plaintext) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + g_free (plaintext); + g_free (diagnostics); + return NULL; + } + + g_free (diagnostics); + + return plaintext; +} + + +/** + * openpgp_encrypt: + * @in: data to encrypt + * @inlen: input length of input data + * @recipients: An array of recipient ids + * @sign: TRUE if you want to sign as well as encrypt + * @userid: userid to use when signing (assuming #sign is TRUE) + * @ex: exception + * + * Returns an allocated string containing the ciphertext. + **/ +gchar * +openpgp_encrypt (const gchar *in, gint inlen, const GPtrArray *recipients, + gboolean sign, const gchar *userid, CamelException *ex) +{ + GPtrArray *recipient_list = NULL; + GPtrArray *argv = NULL; + int retval, r; + char *ciphertext = NULL; + char *diagnostics = NULL; + int passwd_fds[2]; + char passwd_fd[32]; + char *passphrase = NULL; + + if (pgp_type == PGP_TYPE_NONE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return NULL; + } + + if (sign) { + /* we only need a passphrase if we intend on signing */ + passphrase = pgp_get_passphrase (NULL); + if (!passphrase) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("No password provided.")); + return NULL; + } + } + + if (pipe (passwd_fds) < 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return NULL; + } + + argv = g_ptr_array_new (); + switch (pgp_type) { + case PGP_TYPE_GPG: + if (recipients->len == 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No recipients specified")); + return NULL; + } + + recipient_list = g_ptr_array_new (); + for (r = 0; r < recipients->len; r++) { + char *buf, *recipient; + + recipient = recipients->pdata[r]; + buf = g_strdup_printf ("-r %s", recipient); + g_ptr_array_add (recipient_list, buf); + g_free (recipient); + } + + g_ptr_array_add (argv, "gpg"); + + g_ptr_array_add (argv, "--verbose"); + g_ptr_array_add (argv, "--yes"); + g_ptr_array_add (argv, "--batch"); + + g_ptr_array_add (argv, "--armor"); + + for (r = 0; r < recipient_list->len; r++) + g_ptr_array_add (argv, recipient_list->pdata[r]); + + g_ptr_array_add (argv, "--output"); + g_ptr_array_add (argv, "-"); /* output to stdout */ + + g_ptr_array_add (argv, "--encrypt"); + + if (sign) { + g_ptr_array_add (argv, "--sign"); + + g_ptr_array_add (argv, "-u"); + g_ptr_array_add (argv, (gchar *) userid); + + g_ptr_array_add (argv, "--passphrase-fd"); + sprintf (passwd_fd, "%d", passwd_fds[0]); + g_ptr_array_add (argv, passwd_fd); + } + break; + case PGP_TYPE_PGP5: + if (recipients->len == 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No recipients specified")); + return NULL; + } + + recipient_list = g_ptr_array_new (); + for (r = 0; r < recipients->len; r++) { + char *buf, *recipient; + + recipient = recipients->pdata[r]; + buf = g_strdup_printf ("-r %s", recipient); + g_ptr_array_add (recipient_list, buf); + g_free (recipient); + } + + g_ptr_array_add (argv, "pgpe"); + + for (r = 0; r < recipient_list->len; r++) + g_ptr_array_add (argv, recipient_list->pdata[r]); + + g_ptr_array_add (argv, "-f"); + g_ptr_array_add (argv, "-z"); + g_ptr_array_add (argv, "-a"); + g_ptr_array_add (argv, "-o"); + g_ptr_array_add (argv, "-"); /* output to stdout */ + + if (sign) { + g_ptr_array_add (argv, "-s"); + + g_ptr_array_add (argv, "-u"); + g_ptr_array_add (argv, (gchar *) userid); + + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + } + break; + case PGP_TYPE_PGP2: + if (recipients->len == 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No recipients specified")); + return NULL; + } + + recipient_list = g_ptr_array_new (); + for (r = 0; r < recipients->len; r++) { + char *buf, *recipient; + + recipient = recipients->pdata[r]; + buf = g_strdup (recipient); + g_ptr_array_add (recipient_list, buf); + g_free (recipient); + } + + g_ptr_array_add (argv, "pgp"); + g_ptr_array_add (argv, "-f"); + g_ptr_array_add (argv, "-e"); + g_ptr_array_add (argv, "-a"); + g_ptr_array_add (argv, "-o"); + g_ptr_array_add (argv, "-"); + + for (r = 0; r < recipient_list->len; r++) + g_ptr_array_add (argv, recipient_list->pdata[r]); + + if (sign) { + g_ptr_array_add (argv, "-s"); + + g_ptr_array_add (argv, "-u"); + g_ptr_array_add (argv, (gchar *) userid); + + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + } + break; + default: + break; + } + + g_ptr_array_add (argv, NULL); + + retval = crypto_exec_with_passwd (pgp_path, (char **) argv->pdata, + in, inlen, passwd_fds, + passphrase, &ciphertext, NULL, + &diagnostics); + + g_free (passphrase); + g_ptr_array_free (argv, TRUE); + + if (retval != 0 || !*ciphertext) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + g_free (ciphertext); + ciphertext = NULL; + } + + if (recipient_list) { + for (r = 0; r < recipient_list->len; r++) + g_free (recipient_list->pdata[r]); + g_ptr_array_free (recipient_list, TRUE); + } + + g_free (diagnostics); + + return ciphertext; +} + + +/** + * openpgp_clearsign: + * @plaintext: plain readable text to clearsign + * @userid: userid to sign with + * @hash: Preferred hash function (md5 or sha1) + * @ex: exception + * + * Returns an allocated string containing the clearsigned plaintext + * using the preferred hash. + **/ +gchar * +openpgp_clearsign (const gchar *plaintext, const gchar *userid, + PgpHashType hash, CamelException *ex) +{ + char *argv[15]; + char *ciphertext = NULL; + char *diagnostics = NULL; + char *passphrase = NULL; + char *hash_str = NULL; + int passwd_fds[2]; + char passwd_fd[32]; + int retval, i; + + if (pgp_type == PGP_TYPE_NONE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return NULL; + } + + passphrase = pgp_get_passphrase (userid); + if (!passphrase) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("No password provided.")); + return NULL; + } + + if (pipe (passwd_fds) < 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return NULL; + } + + switch (hash) { + case PGP_HASH_TYPE_MD5: + hash_str = "MD5"; + break; + case PGP_HASH_TYPE_SHA1: + hash_str = "SHA1"; + break; + default: + break; + } + + i = 0; + switch (pgp_type) { + case PGP_TYPE_GPG: + argv[i++] = "gpg"; + + argv[i++] = "--clearsign"; + + if (hash_str) { + argv[i++] = "--digest-algo"; + argv[i++] = hash_str; + } + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--armor"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "--passphrase-fd"; + sprintf (passwd_fd, "%d", passwd_fds[0]); + argv[i++] = passwd_fd; + break; + case PGP_TYPE_PGP5: + /* FIXME: modify to respect hash */ + argv[i++] = "pgps"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-f"; + argv[i++] = "-z"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; /* output to stdout */ + + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + break; + case PGP_TYPE_PGP2: + /* FIXME: modify to respect hash */ + argv[i++] = "pgp"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-f"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; + + argv[i++] = "-st"; + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + break; + default: + break; + } + + argv[i++] = NULL; + + retval = crypto_exec_with_passwd (pgp_path, argv, + plaintext, strlen (plaintext), + passwd_fds, passphrase, + &ciphertext, NULL, + &diagnostics); + + g_free (passphrase); + + if (retval != 0 || !*ciphertext) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + g_free (ciphertext); + ciphertext = NULL; + } + + g_free (diagnostics); + + return ciphertext; +} + + +/** + * openpgp_sign: + * @in: input data to sign + * @inlen: length of input data + * @userid: userid to sign with + * @hash: preferred hash type (md5 or sha1) + * @ex: exception + * + * Returns an allocated string containing the detached signature using + * the preferred hash. + **/ +gchar * +openpgp_sign (const gchar *in, gint inlen, const gchar *userid, + PgpHashType hash, CamelException *ex) +{ + char *argv[20]; + char *ciphertext = NULL; + char *diagnostics = NULL; + char *passphrase = NULL; + char *hash_str = NULL; + int passwd_fds[2]; + char passwd_fd[32]; + int retval, i; + + if (pgp_type == PGP_TYPE_NONE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return NULL; + } + + passphrase = pgp_get_passphrase (userid); + if (!passphrase) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("No password provided.")); + return NULL; + } + + if (pipe (passwd_fds) < 0) { + g_free (passphrase); + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return NULL; + } + + switch (hash) { + case PGP_HASH_TYPE_MD5: + hash_str = "MD5"; + break; + case PGP_HASH_TYPE_SHA1: + hash_str = "SHA1"; + break; + default: + break; + } + + i = 0; + switch (pgp_type) { + case PGP_TYPE_GPG: + argv[i++] = "gpg"; + + argv[i++] = "--sign"; + argv[i++] = "-b"; + if (hash_str) { + argv[i++] = "--digest-algo"; + argv[i++] = hash_str; + } + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "--verbose"; + argv[i++] = "--yes"; + argv[i++] = "--batch"; + + argv[i++] = "--armor"; + + argv[i++] = "--output"; + argv[i++] = "-"; /* output to stdout */ + + argv[i++] = "--passphrase-fd"; + sprintf (passwd_fd, "%d", passwd_fds[0]); + argv[i++] = passwd_fd; + break; + case PGP_TYPE_PGP5: + /* FIXME: respect hash */ + argv[i++] = "pgps"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-b"; + argv[i++] = "-f"; + argv[i++] = "-z"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; /* output to stdout */ + + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + break; + case PGP_TYPE_PGP2: + /* FIXME: respect hash */ + argv[i++] = "pgp"; + + if (userid) { + argv[i++] = "-u"; + argv[i++] = (char *) userid; + } + + argv[i++] = "-f"; + argv[i++] = "-a"; + argv[i++] = "-o"; + argv[i++] = "-"; + + argv[i++] = "-sb"; /* create a detached signature */ + sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]); + putenv (passwd_fd); + break; + default: + break; + } + + argv[i++] = NULL; + + retval = crypto_exec_with_passwd (pgp_path, argv, + in, inlen, + passwd_fds, passphrase, + &ciphertext, NULL, + &diagnostics); + + g_free (passphrase); + + if (retval != 0 || !*ciphertext) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + g_free (ciphertext); + ciphertext = NULL; + } + + g_free (diagnostics); + + return ciphertext; +} + +static char * +swrite (const char *data, int len) +{ + char *template; + int fd; + + template = g_strdup ("/tmp/gmime-crypto-XXXXXX"); + fd = mkstemp (template); + if (fd == -1) { + g_free (template); + return NULL; + } + + write (fd, data, len); + close (fd); + + return template; +} + +gboolean +openpgp_verify (const gchar *in, gint inlen, const gchar *sigin, gint siglen, CamelException *ex) +{ + char *argv[20]; + char *cleartext = NULL; + char *diagnostics = NULL; + int passwd_fds[2]; + char passwd_fd[32]; + char *sigfile = NULL; + int retval, i, clearlen; + gboolean valid = TRUE; + + + if (pgp_type == PGP_TYPE_NONE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("No GPG/PGP program available.")); + return FALSE; + } + + if (pipe (passwd_fds) < 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create pipe to GPG/PGP: %s"), + g_strerror (errno)); + return FALSE; + } + + if (sigin != NULL && siglen) { + /* We are going to verify a detached signature so save + the signature to a temp file. */ + sigfile = swrite (sigin, siglen); + if (!sigfile) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Couldn't create temp file: %s"), + g_strerror (errno)); + return FALSE; + } + } + + i = 0; + switch (pgp_type) { + case PGP_TYPE_GPG: + argv[i++] = "gpg"; + + argv[i++] = "--verify"; + + if (sigin != NULL && siglen) + argv[i++] = sigfile; + + argv[i++] = "-"; + + /*argv[i++] = "--verbose";*/ + /*argv[i++] = "--yes";*/ + /*argv[i++] = "--batch";*/ + break; + case PGP_TYPE_PGP5: + argv[i++] = "pgpv"; + + argv[i++] = "-z"; + + if (sigin != NULL && siglen) + argv[i++] = sigfile; + + argv[i++] = "-f"; + + break; + case PGP_TYPE_PGP2: + argv[i++] = "pgp"; + + if (sigin != NULL && siglen) + argv[i++] = sigfile; + + argv[i++] = "-f"; + + break; + default: + break; + } + + argv[i++] = NULL; + + clearlen = 0; + retval = crypto_exec_with_passwd (pgp_path, argv, + in, inlen, + passwd_fds, NULL, + &cleartext, &clearlen, + &diagnostics); + + /* cleanup */ + if (sigfile) { + unlink (sigfile); + g_free (sigfile); + } + + /* FIXME: maybe we should always set an exception? */ + if (retval != 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + "%s", diagnostics); + valid = FALSE; + } + + g_free (diagnostics); + g_free (cleartext); + + return valid; +} diff --git a/mail/openpgp-utils.h b/mail/openpgp-utils.h new file mode 100644 index 0000000000..17c5e22ccf --- /dev/null +++ b/mail/openpgp-utils.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast + * + * Copyright 2000 Helix Code, Inc. (www.helixcode.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __OPENPGP_UTILS_H__ +#define __OPENPGP_UTILS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +typedef enum { + PGP_TYPE_NONE, + PGP_TYPE_PGP2, + PGP_TYPE_PGP5, + PGP_TYPE_GPG +} PgpType; + +typedef enum { + PGP_HASH_TYPE_NONE, + PGP_HASH_TYPE_MD5, + PGP_HASH_TYPE_SHA1 +} PgpHashType; + +typedef gchar* (*PgpPasswdFunc) (const gchar *prompt, gpointer data); + +void openpgp_init (const gchar *path, PgpType type, PgpPasswdFunc callback, gpointer data); + +gboolean openpgp_detect (const gchar *text); + +gboolean openpgp_sign_detect (const gchar *text); + +gchar *openpgp_decrypt (const gchar *ciphertext, gint cipherlen, gint *outlen, CamelException *ex); + +gchar *openpgp_encrypt (const gchar *in, gint inlen, const GPtrArray *recipients, + gboolean sign, const gchar *userid, CamelException *ex); + +gchar *openpgp_clearsign (const gchar *plaintext, const gchar *userid, + PgpHashType hash, CamelException *ex); + +gchar *openpgp_sign (const gchar *in, gint inlen, const gchar *userid, + PgpHashType hash, CamelException *ex); + +gboolean openpgp_verify (const gchar *in, gint inlen, const gchar *sigin, + gint siglen, CamelException *ex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __OPENPGP_UTILS_H__ */ -- cgit v1.2.3