summaryrefslogtreecommitdiffstats
path: root/g13/sh-cmd.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
commiteee068778cb28ecf3c14e1bf843a95547d72c42d (patch)
tree0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /g13/sh-cmd.c
parentInitial commit. (diff)
downloadgnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz
gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip
Adding upstream version 2.2.40.upstream/2.2.40
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'g13/sh-cmd.c')
-rw-r--r--g13/sh-cmd.c917
1 files changed, 917 insertions, 0 deletions
diff --git a/g13/sh-cmd.c b/g13/sh-cmd.c
new file mode 100644
index 0000000..791e3b7
--- /dev/null
+++ b/g13/sh-cmd.c
@@ -0,0 +1,917 @@
+/* sh-cmd.c - The Assuan server for 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 <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "g13-syshelp.h"
+#include <assuan.h>
+#include "../common/i18n.h"
+#include "../common/asshelp.h"
+#include "keyblob.h"
+
+
+/* Local data for this server module. A pointer to this is stored in
+ the CTRL object of each connection. */
+struct server_local_s
+{
+ /* The Assuan context we are working on. */
+ assuan_context_t assuan_ctx;
+
+ /* The malloced name of the device. */
+ char *devicename;
+
+ /* A stream open for read of the device set by the DEVICE command or
+ NULL if no DEVICE command has been used. */
+ estream_t devicefp;
+};
+
+
+
+
+/* Local prototypes. */
+
+
+
+
+/*
+ Helper functions.
+ */
+
+/* Set an error and a description. */
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+#define set_error_fail_cmd() set_error (GPG_ERR_NOT_INITIALIZED, \
+ "not called via userv or unknown user")
+
+
+/* Skip over options. Blanks after the options are also removed. */
+static char *
+skip_options (const char *line)
+{
+ while (spacep (line))
+ line++;
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (*line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ return (char*)line;
+}
+
+
+/* Check whether the option NAME appears in LINE. */
+/* static int */
+/* has_option (const char *line, const char *name) */
+/* { */
+/* const char *s; */
+/* int n = strlen (name); */
+
+/* s = strstr (line, name); */
+/* if (s && s >= skip_options (line)) */
+/* return 0; */
+/* return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
+/* } */
+
+
+/* Helper to print a message while leaving a command. */
+static gpg_error_t
+leave_cmd (assuan_context_t ctx, gpg_error_t err)
+{
+ if (err)
+ {
+ const char *name = assuan_get_command_name (ctx);
+ if (!name)
+ name = "?";
+ if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ log_error ("command '%s' failed: %s\n", name,
+ gpg_strerror (err));
+ else
+ log_error ("command '%s' failed: %s <%s>\n", name,
+ gpg_strerror (err), gpg_strsource (err));
+ }
+ return err;
+}
+
+
+
+
+/* The handler for Assuan OPTION commands. */
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ (void)ctrl;
+ (void)key;
+ (void)value;
+
+ if (ctrl->fail_all_cmds)
+ err = set_error_fail_cmd ();
+ else
+ err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+
+ return err;
+}
+
+
+/* The handler for an Assuan RESET command. */
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void)line;
+
+ xfree (ctrl->server_local->devicename);
+ ctrl->server_local->devicename = NULL;
+ es_fclose (ctrl->server_local->devicefp);
+ ctrl->server_local->devicefp = NULL;
+ ctrl->devti = NULL;
+
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+static const char hlp_finddevice[] =
+ "FINDDEVICE <name>\n"
+ "\n"
+ "Find the device matching NAME. NAME be any identifier from\n"
+ "g13tab permissible for the user. The corresponding block\n"
+ "device is returned using a status line.";
+static gpg_error_t
+cmd_finddevice (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ tab_item_t ti;
+ const char *s;
+ const char *name;
+
+ name = skip_options (line);
+
+ /* Are we allowed to use the given device? We check several names:
+ * 1. The full block device
+ * 2. The label
+ * 3. The final part of the block device if NAME does not have a slash.
+ * 4. The mountpoint
+ */
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (!strcmp (name, ti->blockdev))
+ break;
+ if (!ti)
+ {
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (ti->label && !strcmp (name, ti->label))
+ break;
+ }
+ if (!ti && !strchr (name, '/'))
+ {
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ {
+ s = strrchr (ti->blockdev, '/');
+ if (s && s[1] && !strcmp (name, s+1))
+ break;
+ }
+ }
+ if (!ti)
+ {
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (ti->mountpoint && !strcmp (name, ti->mountpoint))
+ break;
+ }
+
+ if (!ti)
+ {
+ err = set_error (GPG_ERR_NOT_FOUND, "device not configured for user");
+ goto leave;
+ }
+
+ /* Check whether we have permissions to open the device. */
+ {
+ estream_t fp = es_fopen (ti->blockdev, "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ti->blockdev, gpg_strerror (err));
+ goto leave;
+ }
+ es_fclose (fp);
+ }
+
+ err = g13_status (ctrl, STATUS_BLOCKDEV, ti->blockdev, NULL);
+ if (err)
+ return err;
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_device[] =
+ "DEVICE <name>\n"
+ "\n"
+ "Set the device used by further commands.\n"
+ "A device name or a PARTUUID string may be used.\n"
+ "Access to that device (by the g13 system) is locked\n"
+ "until a new DEVICE command or end of this process\n";
+static gpg_error_t
+cmd_device (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ tab_item_t ti;
+ estream_t fp = NULL;
+
+ line = skip_options (line);
+
+/* # warning hardwired to /dev/sdb1 ! */
+/* if (strcmp (line, "/dev/sdb1")) */
+/* { */
+/* err = gpg_error (GPG_ERR_ENOENT); */
+/* goto leave; */
+/* } */
+
+ /* Always close an open device stream of this session. */
+ xfree (ctrl->server_local->devicename);
+ ctrl->server_local->devicename = NULL;
+ es_fclose (ctrl->server_local->devicefp);
+ ctrl->server_local->devicefp = NULL;
+
+ /* Are we allowed to use the given device? */
+ for (ti=ctrl->client.tab; ti; ti = ti->next)
+ if (!strcmp (line, ti->blockdev))
+ break;
+ if (!ti)
+ {
+ err = set_error (GPG_ERR_EACCES, "device not configured for user");
+ goto leave;
+ }
+
+ ctrl->server_local->devicename = xtrystrdup (line);
+ if (!ctrl->server_local->devicename)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+
+ /* Check whether we have permissions to open the device and keep an
+ FD open. */
+ fp = es_fopen (ctrl->server_local->devicename, "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+
+ es_fclose (ctrl->server_local->devicefp);
+ ctrl->server_local->devicefp = fp;
+ fp = NULL;
+ ctrl->devti = ti;
+
+ /* Fixme: Take some kind of lock. */
+
+ leave:
+ es_fclose (fp);
+ if (err)
+ {
+ xfree (ctrl->server_local->devicename);
+ ctrl->server_local->devicename = NULL;
+ ctrl->devti = NULL;
+ }
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_create[] =
+ "CREATE <type>\n"
+ "\n"
+ "Create a new encrypted partition on the current device.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_create (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ estream_t fp = NULL;
+
+ line = skip_options (line);
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_FALSE)
+ err = gpg_error (GPG_ERR_CONFLICT);
+ err = assuan_set_error (ctx, err, "Partition is not empty");
+ goto leave;
+ }
+
+ /* We need a writeable stream to create the container. */
+ fp = es_fopen (ctrl->server_local->devicename, "r+b");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+ if (es_setvbuf (fp, NULL, _IONBF, 0))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error setting '%s' to _IONBF: %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = sh_dmcrypt_create_container (ctrl,
+ ctrl->server_local->devicename,
+ fp);
+ if (es_fclose (fp))
+ {
+ gpg_error_t err2 = gpg_error_from_syserror ();
+ log_error ("error closing '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err2));
+ if (!err)
+ err = err2;
+ }
+ fp = NULL;
+
+ leave:
+ es_fclose (fp);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_getkeyblob[] =
+ "GETKEYBLOB\n"
+ "\n"
+ "Return the encrypted keyblob of the current device.";
+static gpg_error_t
+cmd_getkeyblob (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ void *enckeyblob = NULL;
+ size_t enckeybloblen;
+
+ line = skip_options (line);
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ err = g13_keyblob_read (ctrl->server_local->devicename,
+ &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ err = assuan_send_data (ctx, enckeyblob, enckeybloblen);
+ if (!err)
+ err = assuan_send_data (ctx, NULL, 0); /* Flush */
+
+ leave:
+ xfree (enckeyblob);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_mount[] =
+ "MOUNT <type>\n"
+ "\n"
+ "Mount an encrypted partition on the current device.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_mount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ unsigned char *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ /* We expect that the client already decrypted the keyblob.
+ * Eventually we should move reading of the keyblob to here and ask
+ * the client to decrypt it. */
+ assuan_begin_confidential (ctx);
+ err = assuan_inquire (ctx, "KEYBLOB",
+ &keyblob, &keybloblen, 4 * 1024);
+ assuan_end_confidential (ctx);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version received\n");
+ goto leave;
+ }
+
+ err = sh_dmcrypt_mount_container (ctrl,
+ ctrl->server_local->devicename,
+ tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_umount[] =
+ "UMOUNT <type>\n"
+ "\n"
+ "Unmount an encrypted partition and wipe the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_umount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_dmcrypt_umount_container (ctrl, ctrl->server_local->devicename);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_suspend[] =
+ "SUSPEND <type>\n"
+ "\n"
+ "Suspend an encrypted partition and wipe the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_resume[] =
+ "RESUME <type>\n"
+ "\n"
+ "Resume an encrypted partition and set the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_resume (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ unsigned char *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+
+ line = skip_options (line);
+
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ /* We expect that the client already decrypted the keyblob.
+ * Eventually we should move reading of the keyblob to here and ask
+ * the client to decrypt it. */
+ assuan_begin_confidential (ctx);
+ err = assuan_inquire (ctx, "KEYBLOB",
+ &keyblob, &keybloblen, 4 * 1024);
+ assuan_end_confidential (ctx);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version received\n");
+ goto leave;
+ }
+
+ err = sh_dmcrypt_resume_container (ctrl,
+ ctrl->server_local->devicename,
+ tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_getinfo[] =
+ "GETINFO <what>\n"
+ "\n"
+ "Multipurpose function to return a variety of information.\n"
+ "Supported values for WHAT are:\n"
+ "\n"
+ " version - Return the version of the program.\n"
+ " pid - Return the process id of the server.\n"
+ " showtab - Show the table for the user.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ char *buf;
+
+ if (!strcmp (line, "version"))
+ {
+ const char *s = PACKAGE_VERSION;
+ err = assuan_send_data (ctx, s, strlen (s));
+ }
+ else if (!strcmp (line, "pid"))
+ {
+ char numbuf[50];
+
+ snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+ err = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strncmp (line, "getsz", 5))
+ {
+ unsigned long long nblocks;
+ err = sh_blockdev_getsz (line+6, &nblocks);
+ if (!err)
+ log_debug ("getsz=%llu\n", nblocks);
+ }
+ else if (!strcmp (line, "showtab"))
+ {
+ tab_item_t ti;
+
+ for (ti=ctrl->client.tab; !err && ti; ti = ti->next)
+ {
+ buf = es_bsprintf ("%s %s%s %s %s%s\n",
+ ctrl->client.uname,
+ *ti->blockdev=='/'? "":"partuuid=",
+ ti->blockdev,
+ ti->label? ti->label : "-",
+ ti->mountpoint? " ":"",
+ ti->mountpoint? ti->mountpoint:"");
+ if (!buf)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = assuan_send_data (ctx, buf, strlen (buf));
+ if (!err)
+ err = assuan_send_data (ctx, NULL, 0); /* Flush */
+ }
+ xfree (buf);
+ }
+ }
+ else
+ err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+
+ return leave_cmd (ctx, err);
+}
+
+
+/* This command handler is used for all commands if this process has
+ not been started as expected. */
+static gpg_error_t
+fail_command (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err;
+ const char *name = assuan_get_command_name (ctx);
+
+ (void)line;
+
+ if (!name)
+ name = "?";
+
+ err = set_error_fail_cmd ();
+ log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
+ return err;
+}
+
+
+/* Tell the Assuan library about our commands. */
+static int
+register_commands (assuan_context_t ctx, int fail_all)
+{
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ { "FINDDEVICE", cmd_finddevice, hlp_finddevice },
+ { "DEVICE", cmd_device, hlp_device },
+ { "CREATE", cmd_create, hlp_create },
+ { "GETKEYBLOB", cmd_getkeyblob, hlp_getkeyblob },
+ { "MOUNT", cmd_mount, hlp_mount },
+ { "UMOUNT", cmd_umount, hlp_umount },
+ { "SUSPEND", cmd_suspend,hlp_suspend},
+ { "RESUME", cmd_resume, hlp_resume },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "GETINFO", cmd_getinfo, hlp_getinfo },
+ { NULL }
+ };
+ gpg_error_t err;
+ int i;
+
+ for (i=0; table[i].name; i++)
+ {
+ err = assuan_register_command (ctx, table[i].name,
+ fail_all ? fail_command : table[i].handler,
+ table[i].help);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Startup the server. */
+gpg_error_t
+syshelp_server (ctrl_t ctrl)
+{
+ gpg_error_t err;
+ assuan_fd_t filedes[2];
+ assuan_context_t ctx = NULL;
+
+ /* We use a pipe based server so that we can work from scripts.
+ assuan_init_pipe_server will automagically detect when we are
+ called with a socketpair and ignore FILEDES in this case. */
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+ err = assuan_new (&ctx);
+ if (err)
+ {
+ log_error ("failed to allocate an Assuan context: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ err = assuan_init_pipe_server (ctx, filedes);
+ if (err)
+ {
+ log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = register_commands (ctx, 0 /*FIXME:ctrl->fail_all_cmds*/);
+ if (err)
+ {
+ log_error ("failed to the register commands with Assuan: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ assuan_set_pointer (ctx, ctrl);
+
+ {
+ char *tmp = xtryasprintf ("G13-syshelp %s ready to serve requests "
+ "from %lu(%s)",
+ PACKAGE_VERSION,
+ (unsigned long)ctrl->client.uid,
+ ctrl->client.uname);
+ if (tmp)
+ {
+ assuan_set_hello_line (ctx, tmp);
+ xfree (tmp);
+ }
+ }
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_option_handler (ctx, option_handler);
+
+ ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
+ if (!ctrl->server_local)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ ctrl->server_local->assuan_ctx = ctx;
+
+ while ( !(err = assuan_accept (ctx)) )
+ {
+ err = assuan_process (ctx);
+ if (err)
+ log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
+ }
+ if (err == -1)
+ err = 0;
+ else
+ log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
+
+ leave:
+ reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
+ if (ctrl->server_local)
+ {
+ xfree (ctrl->server_local);
+ ctrl->server_local = NULL;
+ }
+
+ assuan_release (ctx);
+ return err;
+}
+
+
+gpg_error_t
+sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
+ char **r_enckeyblob, size_t *r_enckeybloblen)
+{
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+ gpg_error_t err;
+ unsigned char *enckeyblob;
+ size_t enckeybloblen;
+
+ *r_enckeyblob = NULL;
+
+ /* Send the plaintext. */
+ err = g13_status (ctrl, STATUS_PLAINTEXT_FOLLOWS, NULL);
+ if (err)
+ return err;
+ assuan_begin_confidential (ctx);
+ err = assuan_send_data (ctx, keyblob, keybloblen);
+ if (!err)
+ err = assuan_send_data (ctx, NULL, 0);
+ assuan_end_confidential (ctx);
+ if (!err)
+ err = assuan_write_line (ctx, "END");
+ if (err)
+ {
+ log_error (_("error sending data: %s\n"), gpg_strerror (err));
+ return err;
+ }
+
+ /* Inquire the ciphertext. */
+ err = assuan_inquire (ctx, "ENCKEYBLOB",
+ &enckeyblob, &enckeybloblen, 16 * 1024);
+ if (err)
+ {
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ return err;
+ }
+
+ *r_enckeyblob = enckeyblob;
+ *r_enckeybloblen = enckeybloblen;
+ return 0;
+}
+
+
+/* Send a status line with status ID NO. The arguments are a list of
+ strings terminated by a NULL argument. */
+gpg_error_t
+g13_status (ctrl_t ctrl, int no, ...)
+{
+ gpg_error_t err;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, no);
+
+ err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
+ get_status_string (no), arg_ptr);
+ va_end (arg_ptr);
+ return err;
+}