summaryrefslogtreecommitdiff
path: root/debian/patches/gpg0002-trans-add-transparent-GnuPG-translator.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/gpg0002-trans-add-transparent-GnuPG-translator.patch')
-rw-r--r--debian/patches/gpg0002-trans-add-transparent-GnuPG-translator.patch890
1 files changed, 890 insertions, 0 deletions
diff --git a/debian/patches/gpg0002-trans-add-transparent-GnuPG-translator.patch b/debian/patches/gpg0002-trans-add-transparent-GnuPG-translator.patch
new file mode 100644
index 00000000..323ef96c
--- /dev/null
+++ b/debian/patches/gpg0002-trans-add-transparent-GnuPG-translator.patch
@@ -0,0 +1,890 @@
+From f3dde907bc9bae67710eb47cb9d43748bbee278c Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Thu, 24 Mar 2016 22:55:54 +0100
+Subject: [PATCH hurd 2/4] trans: add transparent GnuPG translator
+
+* trans/Makefile: Add new file.
+* trans/gpg.c: New file.
+* utils/Makefile: Add new file.
+* utils/gpg-env.sh: New file.
+---
+ trans/Makefile | 9 +-
+ trans/gpg.c | 682 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ utils/Makefile | 6 +-
+ utils/gpg-env.sh | 122 ++++++++++
+ 4 files changed, 812 insertions(+), 7 deletions(-)
+ create mode 100644 trans/gpg.c
+ create mode 100644 utils/gpg-env.sh
+
+diff --git a/trans/Makefile b/trans/Makefile
+index e4eba0a..422d499 100644
+--- a/trans/Makefile
++++ b/trans/Makefile
+@@ -21,10 +21,11 @@ makemode := servers
+
+ targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
+ password hello hello-mt streamio fakeroot proxy-defpager remap \
+- mtab identity
++ mtab identity gpg
+ SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \
+ crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \
+- fakeroot.c proxy-defpager.c remap.c mtab.c chroot.c identity.c
++ fakeroot.c proxy-defpager.c remap.c mtab.c chroot.c identity.c \
++ gpg.c
+ OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
+ crashServer.o crash_replyUser.o msgServer.o \
+ default_pagerServer.o default_pagerUser.o \
+@@ -65,8 +66,8 @@ proxy-defpager: default_pagerServer.o default_pagerUser.o
+ streamio: device_replyServer.o
+ symlink: fsysServer.o
+
+-identity: chroot.o
+-fakeroot identity: ../libnetfs/libnetfs.a
++identity gpg: chroot.o
++fakeroot identity gpg: ../libnetfs/libnetfs.a
+ fifo new-fifo: ../libpipe/libpipe.a
+ crash fifo firmlink hello hello-mt ifsock magic mtab new-fifo null password proxy-defpager remap streamio: ../libtrivfs/libtrivfs.a
+ $(targets): ../libfshelp/libfshelp.a \
+diff --git a/trans/gpg.c b/trans/gpg.c
+new file mode 100644
+index 0000000..abdea7c
+--- /dev/null
++++ b/trans/gpg.c
+@@ -0,0 +1,682 @@
++/* gpg -- a translator for encrypting, decrypting, and verifying files
++ Copyright (C) 2016 Free Software Foundation, Inc.
++
++ 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, 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 */
++
++#include <argz.h>
++#include <argp.h>
++#include <dirent.h>
++#include <error.h>
++#include <hurd/netfs.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <fcntl.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <pthread.h>
++#include <hurd/ihash.h>
++#include <hurd/paths.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <unistd.h>
++
++#include <version.h>
++
++#include "chroot.h"
++
++#include "libnetfs/fs_S.h"
++#include "libnetfs/io_S.h"
++#include "libnetfs/fsys_S.h"
++#include "libports/notify_S.h"
++#include "libports/interrupt_S.h"
++
++const char *argp_program_version = STANDARD_HURD_VERSION (gpg);
++
++char *netfs_server_name = "gpg";
++char *netfs_server_version = HURD_VERSION;
++int netfs_maxsymlinks = 16; /* arbitrary */
++
++struct chroot_node
++{
++ unsigned int encrypt:1;
++ file_t shadow_file;
++};
++
++#define GPG_DEFAULT_FILENAME "/usr/bin/gpg2"
++static char* gpg_filename = GPG_DEFAULT_FILENAME;
++static file_t gpg_file;
++
++int symmetric;
++char *recipients;
++size_t recipients_len;
++
++static error_t
++execute_gpg (mach_port_t in, mach_port_t out, mach_port_t extra,
++ char *argz, size_t argz_len, pid_t *pid)
++{
++ error_t err;
++ task_t task;
++ process_t proc;
++ mach_port_t ports[INIT_PORT_MAX];
++ mach_port_t fds[STDERR_FILENO + 2];
++ int ints[INIT_INT_MAX];
++ int i;
++
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ ports[i] = MACH_PORT_NULL;
++ for (i = 0; i < STDERR_FILENO + 2; i++)
++ fds[i] = MACH_PORT_NULL;
++ memset (ints, 0, INIT_INT_MAX * sizeof(int));
++
++ ports[INIT_PORT_CWDIR] = getcwdir ();
++ ports[INIT_PORT_CRDIR] = getcrdir ();
++ ports[INIT_PORT_AUTH] = getauth ();
++ fds[STDIN_FILENO] = in;
++ fds[STDOUT_FILENO] = out;
++ fds[STDERR_FILENO] = getdport (STDERR_FILENO);
++ fds[STDERR_FILENO+1] = extra;
++
++ err = task_create (mach_task_self (), 0, &task);
++ if (err)
++ goto lose;
++
++ proc = getproc ();
++ proc_child (proc, task);
++
++ /* Try and exec GnuPG in TASK... */
++ err = file_exec (gpg_file, task, EXEC_DEFAULTS,
++ argz, argz_len, 0, 0,
++ fds, MACH_MSG_TYPE_COPY_SEND, STDERR_FILENO + 2,
++ ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
++ ints, INIT_INT_MAX, 0, 0, 0, 0);
++ if (err)
++ goto lose;
++
++ err = proc_task2pid (proc, task, pid);
++
++ lose:
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ mach_port_deallocate (mach_task_self (), ports[i]);
++ mach_port_deallocate (mach_task_self (), fds[STDERR_FILENO]);
++ return err;
++}
++
++error_t
++clone_file (file_t file, file_t *new_file)
++{
++ error_t err;
++ off_t newp;
++ retry_type do_retry;
++ string_t retry_name;
++ err = dir_lookup (file, "", O_RDONLY, 0, &do_retry, retry_name, new_file);
++ if (err)
++ return err;
++ return io_seek (*new_file, 0, SEEK_SET, &newp);
++}
++
++static int
++gpg_verify (file_t sigfile, file_t orig_file)
++{
++ error_t err;
++ file_t file;
++ pid_t pid;
++ char *argz = NULL;
++ size_t argz_len = 0;
++ err = clone_file (orig_file, &file);
++ if (err)
++ return 0;
++
++ argz_add (&argz, &argz_len, "gpg2");
++ argz_add (&argz, &argz_len, "--verify");
++ argz_add (&argz, &argz_len, "/dev/fd/3");
++ argz_add (&argz, &argz_len, "-");
++ err = execute_gpg (file, getdport (STDERR_FILENO), sigfile, argz, argz_len, &pid);
++ mach_port_deallocate (mach_task_self (), file);
++ if (err)
++ {
++ error (0, err, "execute_gpg");
++ return 0;
++ }
++
++ int status;
++ while (waitpid (pid, &status, 0) == (pid_t)-1 && errno == EINTR)
++ { }
++
++ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
++}
++
++static int
++gpg_decrypt (file_t cipherfile, file_t plainfile)
++{
++ error_t err;
++ pid_t pid;
++ char *argz = NULL;
++ size_t argz_len = 0;
++
++ argz_add (&argz, &argz_len, "gpg2");
++ argz_add (&argz, &argz_len, "--output");
++ argz_add (&argz, &argz_len, "-");
++ err = execute_gpg (cipherfile, plainfile, MACH_PORT_NULL,
++ argz, argz_len, &pid);
++ if (err)
++ {
++ error (0, err, "execute_gpg");
++ return 0;
++ }
++
++ int status;
++ while (waitpid (pid, &status, 0) == (pid_t)-1 && errno == EINTR)
++ { }
++
++ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
++}
++
++static int
++gpg_encrypt (file_t plainfile, file_t cipherfile)
++{
++ error_t err;
++ pid_t pid;
++ char *argz = NULL;
++ size_t argz_len = 0;
++
++ argz_add (&argz, &argz_len, "gpg2");
++ argz_add (&argz, &argz_len, "--output");
++ argz_add (&argz, &argz_len, "-");
++ if (recipients)
++ {
++ char *recipient;
++ argz_add (&argz, &argz_len, "--encrypt");
++ for (recipient = recipients;
++ recipient;
++ recipient = argz_next (recipients, recipients_len, recipient))
++ {
++ argz_add (&argz, &argz_len, "--recipient");
++ argz_add (&argz, &argz_len, recipient);
++ }
++ }
++ if (symmetric)
++ argz_add (&argz, &argz_len, "--symmetric");
++
++ err = execute_gpg (plainfile, cipherfile, MACH_PORT_NULL,
++ argz, argz_len, &pid);
++ if (err)
++ {
++ error (0, err, "execute_gpg");
++ return 0;
++ }
++
++ int status;
++ while (waitpid (pid, &status, 0) == (pid_t)-1 && errno == EINTR)
++ { }
++
++ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
++}
++
++static error_t
++make_tmp_file (file_t *file)
++{
++ char name[] = "/tmp/gpgtmp-XXXXXX";
++ int fd;
++
++ fd = mkstemp (name);
++ if (fd == -1)
++ return errno;
++ if (unlink (name) == -1)
++ return errno;
++ *file = getdport (fd);
++ close (fd);
++ return 0;
++}
++
++error_t
++netfs_S_dir_lookup (struct protid *diruser,
++ char *filename,
++ int flags,
++ mode_t mode,
++ retry_type *do_retry,
++ char *retry_name,
++ mach_port_t *retry_port,
++ mach_msg_type_name_t *retry_port_type)
++{
++ struct node *dnp, *np;
++ error_t err;
++ struct protid *newpi;
++ struct iouser *user;
++ file_t file;
++ file_t dir;
++
++ if (! diruser)
++ return EOPNOTSUPP;
++
++ dnp = diruser->po->np;
++
++ dir = netfs_node_netnode (dnp)->file;
++ err = dir_lookup (dir, filename,
++ flags & (O_NOLINK|O_RDWR|O_EXEC|O_CREAT|O_EXCL|O_NONBLOCK),
++ mode, do_retry, retry_name, &file);
++ if (err == ENOENT)
++ {
++ file_t gpgfile;
++ char *gpgname;
++ asprintf (&gpgname, "%s.gpg", filename);
++ gpgfile = file_name_lookup_under (dir, gpgname, O_RDONLY, 0);
++ if (MACH_PORT_VALID (gpgfile))
++ {
++ file_t tmpfile;
++ int ok;
++ off_t newp;
++
++ err = make_tmp_file (&tmpfile);
++ if (err)
++ return err;
++
++ ok = gpg_decrypt (gpgfile, tmpfile);
++ mach_port_deallocate (mach_task_self (), gpgfile);
++ if (! ok)
++ {
++ mach_port_deallocate (mach_task_self (), tmpfile);
++ return EIO;
++ }
++
++ err = io_seek (tmpfile, 0, SEEK_SET, &newp);
++ if (err)
++ {
++ mach_port_deallocate (mach_task_self (), tmpfile);
++ return err;
++ }
++
++ *do_retry = FS_RETRY_NORMAL;
++ retry_name[0] = 0;
++ *retry_port = tmpfile;
++ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
++ return 0;
++ }
++ free (gpgname);
++ }
++
++ if (err)
++ return err;
++
++ switch (*do_retry)
++ {
++ case FS_RETRY_NORMAL:
++ break;
++
++ case FS_RETRY_REAUTH:
++ error (0, 0, "FS_RETRY_REAUTH");
++ /* Fallthrough. */
++
++ case FS_RETRY_MAGICAL:
++ *retry_port = file;
++ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
++ return 0;
++
++ default:
++ /* Invalid response to our dir_lookup request. */
++ if (MACH_PORT_VALID (file))
++ mach_port_deallocate (mach_task_self (), file);
++ *retry_port = MACH_PORT_NULL;
++ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
++ return EOPNOTSUPP;
++ }
++
++ /* We have a new port to an underlying node.
++ Find or make our corresponding virtual node. */
++
++ np = 0;
++
++ pthread_mutex_lock (&dnp->lock);
++ err = chroot_new_node (file, sizeof (struct chroot_node), &np);
++ pthread_mutex_unlock (&dnp->lock);
++ if (!err)
++ {
++ chroot_node (np)->encrypt = chroot_node (dnp)->encrypt;
++ chroot_node (np)->shadow_file = MACH_PORT_NULL;
++ err = netfs_validate_stat (np, diruser->user);
++ }
++ if (err)
++ goto lose;
++
++ if (chroot_node (np)->encrypt && flags & O_WRITE
++ && strncmp (filename, "dev/", 4) != 0)
++ {
++ char *ciphname;
++ asprintf (&ciphname, "%s.gpg", filename);
++ if (ciphname == NULL)
++ {
++ err = errno;
++ goto lose;
++ }
++
++ /* Fixup name. */
++ err = dir_link (dir, file, ciphname, 0);
++ free (ciphname);
++ if (err)
++ return err;
++
++ err = dir_unlink (dir, filename);
++ if (err)
++ return err;
++
++ err = make_tmp_file (&chroot_node (np)->shadow_file);
++ if (err)
++ return err;
++ }
++
++ if (retry_name[0] == 0)
++ {
++ file_t sig;
++ char *signame;
++
++ asprintf (&signame, "%s.sig", filename);
++ if (signame == NULL)
++ {
++ err = errno;
++ goto lose;
++ }
++
++ sig = file_name_lookup_under (dir, signame, O_RDONLY, 0);
++ if (MACH_PORT_VALID (sig))
++ {
++ if (! gpg_verify (sig, file))
++ err = EIO;
++ free (signame);
++ signame = NULL;
++ mach_port_deallocate (mach_task_self (), sig);
++ if (err)
++ goto lose;
++ }
++ free (signame);
++ }
++
++ assert (*do_retry == FS_RETRY_NORMAL);
++ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK);
++
++ err = iohelp_dup_iouser (&user, diruser->user);
++ if (!err)
++ {
++ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po),
++ user);
++ if (! newpi)
++ {
++ iohelp_free_iouser (user);
++ err = errno;
++ }
++ else
++ {
++ *retry_port = ports_get_right (newpi);
++ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
++ ports_port_deref (newpi);
++ }
++ }
++
++ lose:
++ if (dir != netfs_node_netnode (dnp)->file)
++ mach_port_deallocate (mach_task_self (), dir);
++ if (np != NULL)
++ netfs_nput (np);
++ return err;
++}
++
++error_t
++netfs_attempt_mkfile (struct iouser *user, struct node *dir,
++ mode_t mode, struct node **np)
++{
++ file_t newfile;
++ error_t err = dir_mkfile (netfs_node_netnode (dir)->file, O_RDWR|O_EXEC,
++ mode, &newfile);
++ pthread_mutex_unlock (&dir->lock);
++ if (err)
++ return err;
++
++ err = chroot_new_node (newfile, sizeof (struct chroot_node), np);
++ if (err)
++ return err;
++
++ if (chroot_node (dir)->encrypt)
++ {
++ err = make_tmp_file (&chroot_node (*np)->shadow_file);
++ if (err)
++ return err;
++ }
++ else
++ chroot_node (*np)->shadow_file = MACH_PORT_NULL;
++
++ pthread_mutex_unlock (&(*np)->lock);
++ return err;
++}
++
++error_t
++netfs_attempt_read (struct iouser *cred, struct node *np,
++ off_t offset, size_t *len, void *data)
++{
++ struct chroot_node *n = chroot_node (np);
++ char *buf = data;
++ error_t err = io_read (n->shadow_file ?: netfs_node_netnode (np)->file,
++ &buf, len, offset, *len);
++ if (err == 0 && buf != data)
++ {
++ memcpy (data, buf, *len);
++ munmap (buf, *len);
++ }
++ return err;
++}
++
++error_t
++netfs_attempt_write (struct iouser *cred, struct node *np,
++ off_t offset, size_t *len, void *data)
++{
++ struct chroot_node *n = chroot_node (np);
++ return io_write (n->shadow_file ?: netfs_node_netnode (np)->file,
++ data, *len, offset, len);
++}
++
++error_t
++netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
++{
++ error_t err;
++ file_t gpg_file = netfs_node_netnode (np)->file;
++ struct chroot_node *n = chroot_node (np);
++
++ err = file_sync (n->shadow_file ?: gpg_file, wait, 0);
++ if (err)
++ return err;
++
++ if (wait && MACH_PORT_VALID (n->shadow_file))
++ {
++ file_t shadow_file;
++ off_t newp;
++
++ err = clone_file (n->shadow_file, &shadow_file);
++ if (err)
++ return err;
++
++ err = io_seek (gpg_file, 0, SEEK_SET, &newp);
++ if (err)
++ return err;
++
++ err = gpg_encrypt (shadow_file, gpg_file);
++ if (err)
++ return err;
++
++ mach_port_deallocate (mach_task_self (), shadow_file);
++ err = file_sync (gpg_file, wait, 0);
++ }
++
++ return err;
++}
++
++
++error_t
++netfs_attempt_link (struct iouser *user, struct node *dir,
++ struct node *file, char *name, int excl)
++{
++ /* XXX translate name if file is encrypted. */
++ return dir_link (netfs_node_netnode (dir)->file,
++ netfs_node_netnode (file)->file, name, excl);
++}
++
++void
++chroot_node_norefs (struct node *np)
++{
++ error_t err;
++ file_t gpg_file = netfs_node_netnode (np)->file;
++ struct chroot_node *n = chroot_node (np);
++ if (n->encrypt)
++ {
++ off_t newp;
++
++ err = io_seek (n->shadow_file, 0, SEEK_SET, &newp);
++ if (err)
++ return;
++
++ err = io_seek (gpg_file, 0, SEEK_SET, &newp);
++ if (err)
++ return;
++
++ err = gpg_encrypt (n->shadow_file, gpg_file);
++ if (err)
++ return;
++
++ mach_port_deallocate (mach_task_self (), n->shadow_file);
++ }
++}
++
++
++error_t
++netfs_get_dirents (struct iouser *cred, struct node *dir,
++ int entry, int nentries, char **data,
++ mach_msg_type_number_t *datacnt,
++ vm_size_t bufsize, int *amt)
++{
++ error_t err;
++ char *ptr;
++ struct dirent *dent;
++ err = dir_readdir (netfs_node_netnode (dir)->file, data, datacnt,
++ entry, nentries, bufsize, amt);
++ if (err)
++ return err;
++
++ for (ptr = *data, dent = (struct dirent *) ptr;
++ ptr - *data < *datacnt;
++ ptr += dent->d_reclen, dent = (struct dirent *) ptr)
++ {
++ size_t len = strlen (dent->d_name);
++ if (len > 4 && strcmp (&dent->d_name[len - 4], ".gpg") == 0)
++ memset (&dent->d_name[len - 4], 0, 4);
++ }
++
++ return 0;
++}
++
++#define OPT_GPG_PROGRAM -1
++
++static struct argp_option options[] =
++{
++ {NULL, 0, NULL, 0, "How to encrypt:", 1},
++ {"recipient", 'r', "NAME", 0, "Encrypt for user NAME. "
++ "Can be given multiple times.", 1},
++ {"symmetric", 'c', NULL, 0, "Encrypt with a password.", 1},
++ {NULL, 0, NULL, 0, "Advanced options:", 2},
++ {"gpg-program", OPT_GPG_PROGRAM, "FILENAME", 0,
++ "Path to the GNU Privacy Guard executable ["GPG_DEFAULT_FILENAME"].", 2},
++ {0}
++};
++
++static char *args_doc = "";
++static char *doc = "Transparently encrypt, decrypt, and verify signatures"
++ " using GnuPG."
++ "\vFor everyday use, the commands 'encrypt', 'decrypt', and 'verify'"
++ " offer a simple and convenient frontend for this translator.";
++
++static error_t
++parse_opt (int key, char *arg, struct argp_state *state)
++{
++ error_t err;
++ switch (key)
++ {
++ case 'r':
++ err = argz_add (&recipients, &recipients_len, arg);
++ if (err)
++ error (1, err, "argz_add");
++ break;
++
++ case 'c':
++ symmetric = 1;
++ break;
++
++ case OPT_GPG_PROGRAM:
++ gpg_filename = arg;
++ break;
++
++ default:
++ return ARGP_ERR_UNKNOWN;
++ }
++ return 0;
++}
++
++int
++main (int argc, char **argv)
++{
++ error_t err;
++ mach_port_t bootstrap;
++
++ struct argp argp =
++ {
++ .options = options,
++ .parser = parse_opt,
++ .args_doc = args_doc,
++ .doc = doc,
++ };
++
++ /* Parse our command line arguments (all none of them). */
++ argp_parse (&argp, argc, argv, 0, 0, 0);
++
++ task_get_bootstrap_port (mach_task_self (), &bootstrap);
++ if (! MACH_PORT_VALID (bootstrap))
++ error (1, 0, "Must be started as a translator");
++
++ gpg_file = file_name_lookup (gpg_filename, O_EXEC, 0);
++ if (! MACH_PORT_VALID (gpg_file))
++ error (1, errno, "%s", gpg_filename);
++
++ netfs_init ();
++ chroot_init ();
++
++ /* Get our underlying node (we presume it's a directory) and use
++ that to make the root node of the filesystem. */
++ err = chroot_new_node (netfs_startup (bootstrap, O_READ),
++ sizeof (struct chroot_node), &netfs_root_node);
++ if (err)
++ error (5, err, "Cannot create root node");
++
++ err = netfs_validate_stat (netfs_root_node, 0);
++ if (err)
++ error (6, err, "Cannot stat underlying node");
++
++ netfs_root_node->nn_stat.st_mode &= ~(S_IPTRANS | S_IATRANS);
++ netfs_root_node->nn_stat.st_mode |= S_IROOT;
++ pthread_mutex_unlock (&netfs_root_node->lock);
++
++ chroot_node (netfs_root_node)->encrypt = 1;
++ chroot_node (netfs_root_node)->shadow_file = MACH_PORT_NULL;
++
++ netfs_server_loop (); /* Never returns. */
++
++ /*NOTREACHED*/
++ return 0;
++}
+diff --git a/utils/Makefile b/utils/Makefile
+index d2ef9e8..aa6ff34 100644
+--- a/utils/Makefile
++++ b/utils/Makefile
+@@ -22,16 +22,16 @@ targets = shd ps settrans showtrans syncfs fsysopts \
+ storeinfo login w uptime ids loginpr sush vmstat portinfo \
+ devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \
+ storeread msgport rpctrace mount gcore fakeauth fakeroot remap \
+- umount nullauth rpcscan vmallocate
++ umount nullauth rpcscan vmallocate gpg-env
+
+-special-targets = loginpr sush uptime fakeroot remap
++special-targets = loginpr sush uptime fakeroot remap gpg-env
+ SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
+ fsysopts.c storeinfo.c login.c loginpr.sh sush.sh w.c \
+ uptime.sh psout.c ids.c vmstat.c portinfo.c devprobe.c vminfo.c \
+ parse.c frobauth.c frobauth-mod.c setauth.c pids.c nonsugid.c \
+ unsu.c ftpcp.c ftpdir.c storeread.c storecat.c msgport.c \
+ rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh remap.sh \
+- nullauth.c match-options.c msgids.c rpcscan.c
++ nullauth.c match-options.c msgids.c rpcscan.c gpg-env.sh
+
+ OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
+ HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
+diff --git a/utils/gpg-env.sh b/utils/gpg-env.sh
+new file mode 100644
+index 0000000..cd3c9d5
+--- /dev/null
++++ b/utils/gpg-env.sh
+@@ -0,0 +1,122 @@
++#!/bin/sh
++# Execute a command in an environment which encrypts, decrypts, and
++# verifies files on demand.
++#
++# Copyright (C) 2016 Free Software Foundation, Inc.
++#
++# This file is part of the GNU Hurd.
++#
++# The GNU Hurd 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, or (at
++# your option) any later version.
++#
++# The GNU Hurd 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++#
++
++USAGE="Usage:
++ [gpg-env] encrypt for RECIPIENT [RECIPIENT...] -- [OPTION...] [COMMAND...]
++ [gpg-env] encrypt with password [OPTION...] [COMMAND...]
++ [gpg-env] decrypt [OPTION...] [COMMAND...]
++ [gpg-env] decrypt with password [OPTION...] [COMMAND...]
++ [gpg-env] verify [OPTION...] [COMMAND...]"
++DOC="Execute COMMAND in an environment where files are automatically
++encrypted, decrypted and verified."
++
++help()
++{
++ [ "$1" ] && echo "$1
++"
++ echo "$USAGE"
++ echo ""
++ echo "$DOC"
++ echo ""
++ echo " -?, --help Give this help list"
++ echo " --usage Give a short usage message"
++ echo " -V, --version Print program version"
++ [ "$1" ] && exit 1 || exit 0
++}
++
++if [ "$(basename $0)" = "gpg-env.sh" ] \
++ || [ "$(basename $0)" = "gpg-env" ]; then
++ ACTION="$1"
++ if [ ! "$ACTION" ]; then
++ help "No action given."
++ fi
++ shift
++else
++ ACTION="$(basename $0)"
++fi
++
++case "$ACTION" in
++ "encrypt") ;;
++ "decrypt") ;;
++ "verify") ;;
++ *)
++ help "Invalid action '$ACTION'."
++esac
++
++ENCRYPT=""
++if [ "$ACTION" = "encrypt" ]; then
++ if [ "$1" = "with" ] && [ "$2" = "password" ]; then
++ ENCRYPT="--symmetric"
++ shift 2
++ elif [ "$1" = "for" ]; then
++ shift
++ while [ "$#" -gt 0 ] && [ "x$1" != "x--" ]; do
++ ENCRYPT="$ENCRYPT --recipient $1"
++ shift
++ done
++ if [ "$ENCRYPT" = "" ]; then
++ echo "No recipients given."
++ exit 1
++ fi
++ if [ "x$1" = "x--" ]; then
++ shift
++ elif [ "$#" -eq 0 ]; then
++ # it's ok if there are no more arguments
++ :
++ else
++ echo "Recipient list must be terminated using '--'."
++ exit 1
++ fi
++ fi
++fi
++
++while [ "$#" -gt 0 ]; do
++ case "$1" in
++ --help|"-?")
++ help
++ ;;
++ --usage)
++ echo "$USAGE"
++ echo "Options: [-V?] [--help] [--usage] [--version]"
++ exit 0;;
++ --version|-V)
++ echo "STANDARD_HURD_VERSION_gpg-env_"; exit 0;;
++ --)
++ shift
++ break
++ ;;
++ *)
++ break
++ esac
++done
++
++if [ $# -eq 0 ]; then
++ set -- ${SHELL:-/bin/sh}
++fi
++
++# We exec settrans, which execs the target command in the chroot
++# context provided by /hurd/gpg.
++exec /bin/settrans \
++ --chroot-chdir "$PWD" \
++ --chroot "$@" -- \
++ / /hurd/gpg $ENCRYPT
+--
+2.1.4
+