From f3dde907bc9bae67710eb47cb9d43748bbee278c Mon Sep 17 00:00:00 2001 From: Justus Winter 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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