diff options
Diffstat (limited to 'g13/call-syshelp.c')
-rw-r--r-- | g13/call-syshelp.c | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/g13/call-syshelp.c b/g13/call-syshelp.c new file mode 100644 index 0000000..b160ba3 --- /dev/null +++ b/g13/call-syshelp.c @@ -0,0 +1,631 @@ +/* call-syshelp.c - Communication with g13-syshelp + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG 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 3 of the License, or + * (at your option) any later version. + * + * GnuPG 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, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <assert.h> +#include <npth.h> + +#include "g13.h" +#include <assuan.h> +#include "../common/i18n.h" +#include "g13tuple.h" +#include "keyblob.h" +#include "../common/membuf.h" +#include "create.h" +#include "call-syshelp.h" + + +/* Local data for this module. A pointer to this is stored in the + CTRL object of each connection. */ +struct call_syshelp_s +{ + assuan_context_t assctx; /* The Assuan context for the current + g13-syshep connection. */ +}; + + +/* Parameter used with the CREATE command. */ +struct create_parm_s +{ + assuan_context_t ctx; + ctrl_t ctrl; + membuf_t plaintext; + unsigned int expect_plaintext:1; + unsigned int got_plaintext:1; +}; + + +/* Parameter used with the MOUNT command. */ +struct mount_parm_s +{ + assuan_context_t ctx; + ctrl_t ctrl; + const void *keyblob; + size_t keybloblen; +}; + + + + + +/* Fork off the syshelp tool if this has not already been done. On + success stores the current Assuan context for the syshelp tool at + R_CTX. */ +static gpg_error_t +start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx; + assuan_fd_t no_close_list[3]; + int i; + + *r_ctx = NULL; + + if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx)) + return 0; /* Already set. */ + + if (opt.verbose) + log_info ("starting a new syshelp\n"); + + if (!ctrl->syshelp_local) + { + ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local); + if (!ctrl->syshelp_local) + return gpg_error_from_syserror (); + } + + if (es_fflush (NULL)) + { + err = gpg_error_from_syserror (); + log_error ("error flushing pending output: %s\n", gpg_strerror (err)); + return err; + } + + i = 0; + if (log_get_fd () != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); + no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr)); + no_close_list[i] = ASSUAN_INVALID_FD; + + err = assuan_new (&ctx); + if (err) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (err)); + return err; + } + + /* Call userv to start g13-syshelp. This userv script needs to be + * installed under the name "gnupg-g13-syshelp": + * + * if ( glob service-user root + * ) + * reset + * suppress-args + * execute /home/wk/b/gnupg/g13/g13-syshelp -v + * else + * error Nothing to do for this service-user + * fi + * quit + */ + { + const char *argv[4]; + + argv[0] = "userv"; + argv[1] = "root"; + argv[2] = "gnupg-g13-syshelp"; + argv[3] = NULL; + + err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv, + no_close_list, NULL, NULL, 0); + } + if (err) + { + log_error ("can't connect to '%s': %s %s\n", + "g13-syshelp", gpg_strerror (err), gpg_strsource (err)); + log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n"); + assuan_release (ctx); + return err; + } + + *r_ctx = ctrl->syshelp_local->assctx = ctx; + + if (DBG_IPC) + log_debug ("connection to g13-syshelp established\n"); + + return 0; +} + + +/* Release local resources associated with CTRL. */ +void +call_syshelp_release (ctrl_t ctrl) +{ + if (!ctrl) + return; + if (ctrl->syshelp_local) + { + assuan_release (ctrl->syshelp_local->assctx); + ctrl->syshelp_local->assctx = NULL; + xfree (ctrl->syshelp_local); + ctrl->syshelp_local = NULL; + } +} + + + +/* Staus callback for call_syshelp_find_device. */ +static gpg_error_t +finddevice_status_cb (void *opaque, const char *line) +{ + char **r_blockdev = opaque; + char *p; + + if ((p = has_leading_keyword (line, "BLOCKDEV")) && *p && !*r_blockdev) + { + *r_blockdev = xtrystrdup (p); + if (!*r_blockdev) + return gpg_error_from_syserror (); + } + + return 0; +} + + +/* Send the FINDDEVICE command to the syshelper. On success the name + * of the block device is stored at R_BLOCKDEV. */ +gpg_error_t +call_syshelp_find_device (ctrl_t ctrl, const char *name, char **r_blockdev) +{ + gpg_error_t err; + assuan_context_t ctx; + char *line = NULL; + char *blockdev = NULL; /* The result. */ + + *r_blockdev = NULL; + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + line = xtryasprintf ("FINDDEVICE %s", name); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, + finddevice_status_cb, &blockdev); + if (err) + goto leave; + if (!blockdev) + { + log_error ("status line for successful FINDDEVICE missing\n"); + err = gpg_error (GPG_ERR_UNEXPECTED); + goto leave; + } + *r_blockdev = blockdev; + blockdev = NULL; + + leave: + xfree (blockdev); + xfree (line); + return err; +} + + + +static gpg_error_t +getkeyblob_data_cb (void *opaque, const void *data, size_t datalen) +{ + membuf_t *mb = opaque; + + if (data) + put_membuf (mb, data, datalen); + + return 0; +} + + +/* Send the GTEKEYBLOB command to the syshelper. On success the + * encrypted keyblpob is stored at (R_ENCKEYBLOB,R_ENCKEYBLOBLEN). */ +gpg_error_t +call_syshelp_get_keyblob (ctrl_t ctrl, + void **r_enckeyblob, size_t *r_enckeybloblen) +{ + gpg_error_t err; + assuan_context_t ctx; + membuf_t mb; + + *r_enckeyblob = NULL; + *r_enckeybloblen = 0; + init_membuf (&mb, 512); + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + err = assuan_transact (ctx, "GETKEYBLOB", + getkeyblob_data_cb, &mb, + NULL, NULL, NULL, NULL); + if (err) + goto leave; + *r_enckeyblob = get_membuf (&mb, r_enckeybloblen); + if (!*r_enckeyblob) + err = gpg_error_from_syserror (); + + leave: + xfree (get_membuf (&mb, NULL)); + return err; +} + + + +/* Send the DEVICE command to the syshelper. FNAME is the name of the + device. */ +gpg_error_t +call_syshelp_set_device (ctrl_t ctrl, const char *fname) +{ + gpg_error_t err; + assuan_context_t ctx; + char *line = NULL; + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + line = xtryasprintf ("DEVICE %s", fname); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + + leave: + xfree (line); + return err; +} + + + +static gpg_error_t +create_status_cb (void *opaque, const char *line) +{ + struct create_parm_s *parm = opaque; + + if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS")) + parm->expect_plaintext = 1; + + return 0; +} + + +static gpg_error_t +create_data_cb (void *opaque, const void *data, size_t datalen) +{ + struct create_parm_s *parm = opaque; + gpg_error_t err = 0; + + if (!parm->expect_plaintext) + { + log_error ("status line for data missing\n"); + err = gpg_error (GPG_ERR_UNEXPECTED); + } + else if (data) + { + put_membuf (&parm->plaintext, data, datalen); + } + else + { + parm->expect_plaintext = 0; + parm->got_plaintext = 1; + } + + return err; +} + + +static gpg_error_t +create_inq_cb (void *opaque, const char *line) +{ + struct create_parm_s *parm = opaque; + gpg_error_t err; + + if (has_leading_keyword (line, "ENCKEYBLOB")) + { + void *plaintext; + size_t plaintextlen; + + if (!parm->got_plaintext) + err = gpg_error (GPG_ERR_UNEXPECTED); + else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen))) + err = gpg_error_from_syserror (); + else + { + void *ciphertext; + size_t ciphertextlen; + + log_printhex (plaintext, plaintextlen, "plain"); + err = g13_encrypt_keyblob (parm->ctrl, + plaintext, plaintextlen, + &ciphertext, &ciphertextlen); + wipememory (plaintext, plaintextlen); + xfree (plaintext); + if (err) + log_error ("error encrypting keyblob: %s\n", gpg_strerror (err)); + else + { + err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen); + xfree (ciphertext); + if (err) + log_error ("sending ciphertext to g13-syshelp failed: %s\n", + gpg_strerror (err)); + } + } + } + else + err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + + return err; +} + + +/* Run the CREATE command on the current device. CONTTYPES gives the + requested content type for the new container. */ +gpg_error_t +call_syshelp_run_create (ctrl_t ctrl, int conttype) +{ + gpg_error_t err; + assuan_context_t ctx; + struct create_parm_s parm; + + memset (&parm, 0, sizeof parm); + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + /* tty_get ("waiting for debugger"); */ + /* tty_kill_prompt (); */ + + parm.ctx = ctx; + parm.ctrl = ctrl; + init_membuf (&parm.plaintext, 512); + if (conttype == CONTTYPE_DM_CRYPT) + { + err = assuan_transact (ctx, "CREATE dm-crypt", + create_data_cb, &parm, + create_inq_cb, &parm, + create_status_cb, &parm); + } + else + { + log_error ("invalid backend type %d given\n", conttype); + err = GPG_ERR_INTERNAL; + goto leave; + } + + leave: + xfree (get_membuf (&parm.plaintext, NULL)); + return err; +} + + + +static gpg_error_t +mount_status_cb (void *opaque, const char *line) +{ + struct mount_parm_s *parm = opaque; + + /* Nothing right now. */ + (void)parm; + (void)line; + + return 0; +} + + +/* Inquire callback for MOUNT and RESUME. */ +static gpg_error_t +mount_inq_cb (void *opaque, const char *line) +{ + struct mount_parm_s *parm = opaque; + gpg_error_t err; + + if (has_leading_keyword (line, "KEYBLOB")) + { + int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL); + + if (setconfidential) + assuan_begin_confidential (parm->ctx); + err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen); + if (setconfidential) + assuan_end_confidential (parm->ctx); + if (err) + log_error ("sending keyblob to g13-syshelp failed: %s\n", + gpg_strerror (err)); + } + else + err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + + return err; +} + + +/* + * Run the MOUNT command on the current device. CONTTYPES gives the + * requested content type for the new container. MOUNTPOINT the + * desired mount point or NULL for default. + */ +gpg_error_t +call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint, + tupledesc_t tuples) +{ + gpg_error_t err; + assuan_context_t ctx; + struct mount_parm_s parm; + + memset (&parm, 0, sizeof parm); + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + /* tty_get ("waiting for debugger"); */ + /* tty_kill_prompt (); */ + + parm.ctx = ctx; + parm.ctrl = ctrl; + if (conttype == CONTTYPE_DM_CRYPT) + { + ref_tupledesc (tuples); + parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen); + err = assuan_transact (ctx, "MOUNT dm-crypt", + NULL, NULL, + mount_inq_cb, &parm, + mount_status_cb, &parm); + unref_tupledesc (tuples); + } + else + { + (void)mountpoint; /* Not used. */ + log_error ("invalid backend type %d given\n", conttype); + err = GPG_ERR_INTERNAL; + goto leave; + } + + leave: + return err; +} + + + +/* + * Run the UMOUNT command on the current device. CONTTYPES gives the + * content type of the container (fixme: Do we really need this?). + */ +gpg_error_t +call_syshelp_run_umount (ctrl_t ctrl, int conttype) +{ + gpg_error_t err; + assuan_context_t ctx; + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + if (conttype == CONTTYPE_DM_CRYPT) + { + err = assuan_transact (ctx, "UMOUNT dm-crypt", + NULL, NULL, + NULL, NULL, + NULL, NULL); + } + else + { + log_error ("invalid backend type %d given\n", conttype); + err = GPG_ERR_INTERNAL; + goto leave; + } + + leave: + return err; +} + + + +/* + * Run the SUSPEND command on the current device. CONTTYPES gives the + * requested content type for the new container. + */ +gpg_error_t +call_syshelp_run_suspend (ctrl_t ctrl, int conttype) +{ + gpg_error_t err; + assuan_context_t ctx; + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + if (conttype == CONTTYPE_DM_CRYPT) + { + err = assuan_transact (ctx, "SUSPEND dm-crypt", + NULL, NULL, + NULL, NULL, + NULL, NULL); + } + else + { + log_error ("invalid backend type %d given\n", conttype); + err = GPG_ERR_INTERNAL; + goto leave; + } + + leave: + return err; +} + + + +/* Run the RESUME command on the current device. CONTTYPES gives the + requested content type for the container. */ +gpg_error_t +call_syshelp_run_resume (ctrl_t ctrl, int conttype, tupledesc_t tuples) +{ + gpg_error_t err; + assuan_context_t ctx; + struct mount_parm_s parm; + + memset (&parm, 0, sizeof parm); + + err = start_syshelp (ctrl, &ctx); + if (err) + goto leave; + + /* tty_get ("waiting for debugger"); */ + /* tty_kill_prompt (); */ + + parm.ctx = ctx; + parm.ctrl = ctrl; + if (conttype == CONTTYPE_DM_CRYPT) + { + ref_tupledesc (tuples); + parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen); + err = assuan_transact (ctx, "RESUME dm-crypt", + NULL, NULL, + mount_inq_cb, &parm, + NULL, NULL); + unref_tupledesc (tuples); + } + else + { + log_error ("invalid backend type %d given\n", conttype); + err = GPG_ERR_INTERNAL; + goto leave; + } + + leave: + return err; +} |