mirror of
git://git.gnupg.org/gnupg.git
synced 2024-09-07 12:56:21 +02:00
![Werner Koch](/assets/img/avatar_default.png)
* common/asshelp2.c (vprint_assuan_status_strings): New. (print_assuan_status_strings): New. * agent/command.c (agent_write_status): Replace by call to new function. * dirmngr/server.c (dirmngr_status): Ditto. * g13/server.c (g13_status): Ditto. * g13/sh-cmd.c (g13_status): Ditto. * sm/server.c (gpgsm_status2): Ditto. * scd/command.c (send_status_info): Bump up N. -- This fixes a potential overflow if LFs are passed to the status string functions. This is actually not the case and would be wrong because neither the truncating in libassuan or our escaping is not the Right Thing. In any case the functions need to be more robust and comply to the promised interface. Thus the code has been factored out to a helper function and N has been bumped up correctly and checked in all cases. For some uses this changes the behaviour in the error case (i.e. CR or LF passed): It will now always be C-escaped and not passed to libassuan which would truncate the line at the first LF. Reported-by: private_pers
918 lines
23 KiB
C
918 lines
23 KiB
C
/* 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;
|
||
}
|