mount does now work in server and standalone mode.

Implemented a signal handler.
This commit is contained in:
Werner Koch 2009-10-14 17:06:10 +00:00
parent 536b6ab09f
commit 1445c15ed1
9 changed files with 1011 additions and 112 deletions

View File

@ -30,6 +30,7 @@ g13_SOURCES = \
g13.c g13.h \
keyblob.h \
utils.c utils.h \
server.c server.h \
create.c create.h \
mount.c mount.h \
call-gpg.c call-gpg.h \

View File

@ -262,7 +262,8 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
pgmname = "/usr/bin/encfs";
idx = 0;
argv[idx++] = "-f";
argv[idx++] = "-v";
if (opt.verbose)
argv[idx++] = "-v";
argv[idx++] = "--stdinpass";
argv[idx++] = "--annotate";
argv[idx++] = rawdir;

View File

@ -39,7 +39,7 @@
/* Create a new blob with all the session keys and other meta
information which are to be stored encrypted in the crypto
container header. On success the malloced blob is stored at R_BLOB
and its length at R_BLOBLEN. On error en error ocde is returned
and its length at R_BLOBLEN. On error an error code is returned
and (R_BLOB,R_BLOBLEN) are set to (NULL,0).
The format of this blob is a sequence of tag-length-value tuples.

314
g13/g13.c
View File

@ -36,8 +36,9 @@
#include "sysutils.h"
#include "gc-opt-flags.h"
#include "keyblob.h"
#include "./runner.h"
#include "./create.h"
#include "server.h"
#include "runner.h"
#include "create.h"
#include "./mount.h"
@ -51,6 +52,7 @@ enum cmd_and_opt_values {
aCreate,
aMount,
aUmount,
aServer,
oOptions,
oDebug,
@ -102,6 +104,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aCreate, "create", N_("Create a new file system container")),
ARGPARSE_c (aMount, "mount", N_("Mount a file system container") ),
ARGPARSE_c (aUmount, "umount", N_("Unmount a file system container") ),
ARGPARSE_c (aServer, "server", N_("Run in server mode")),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
@ -168,6 +171,10 @@ static ARGPARSE_OPTS opts[] = {
};
/* The timer tick interval used by the idle task. */
#define TIMERTICK_INTERVAL_SEC (1)
/* Global variable to keep an error count. */
int g13_errors_seen = 0;
@ -178,11 +185,23 @@ static int maybe_setuid = 1;
static const char *debug_level;
static unsigned int debug_value;
/* Flag to indicate that a shutdown was requested. */
static int shutdown_pending;
/* The thread id of the idle task. */
static pth_t idle_task_tid;
static void set_cmd (enum cmd_and_opt_values *ret_cmd,
enum cmd_and_opt_values new_cmd );
static void emergency_cleanup (void);
static void start_idle_task (void);
static void join_idle_task (void);
/* Begin Pth wrapper functions. */
GCRY_THREAD_OPTION_PTH_IMPL;
static int fixed_gcry_pth_init (void)
@ -475,6 +494,7 @@ main ( int argc, char **argv)
nokeysetup = 1;
break;
case aServer:
case aMount:
case aUmount:
nokeysetup = 1;
@ -591,9 +611,9 @@ main ( int argc, char **argv)
if (greeting)
{
fprintf(stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) );
fprintf(stderr, "%s\n", strusage(15) );
fprintf (stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) );
fprintf (stderr, "%s\n", strusage(15) );
}
if (may_coredump && !opt.quiet)
@ -680,24 +700,25 @@ main ( int argc, char **argv)
configuration file is valid. */
break;
case aServer:
{
start_idle_task ();
err = g13_server (&ctrl);
if (err)
log_error ("server exited with error: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
}
break;
case aCreate: /* Create a new container. */
{
if (argc != 1)
wrong_args ("--create filename");
start_idle_task ();
err = g13_create_container (&ctrl, argv[0]);
if (err)
log_error ("error creating a new container: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
else
{
unsigned int n;
while ((n = runner_get_threads ()))
{
log_info ("number of running threads: %u\n", n);
pth_sleep (5);
}
}
}
break;
@ -705,20 +726,11 @@ main ( int argc, char **argv)
{
if (argc != 1 && argc != 2 )
wrong_args ("--mount filename [mountpoint]");
start_idle_task ();
err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL);
if (err)
log_error ("error mounting container `%s': %s <%s>\n",
*argv, gpg_strerror (err), gpg_strsource (err));
else
{
unsigned int n;
while ((n = runner_get_threads ()))
{
log_info ("number of running threads: %u\n", n);
pth_sleep (5);
}
}
}
break;
@ -727,6 +739,9 @@ main ( int argc, char **argv)
break;
}
if (!err)
join_idle_task ();
/* Print the audit result if needed. */
if (auditlog && auditfp)
{
@ -741,6 +756,7 @@ main ( int argc, char **argv)
return 8; /*NOTREACHED*/
}
/* Note: This function is used by signal handlers!. */
static void
emergency_cleanup (void)
@ -766,6 +782,7 @@ g13_exit (int rc)
}
/* Store defaults into the per-connection CTRL object. */
void
g13_init_default_ctrl (struct server_control_s *ctrl)
{
@ -773,84 +790,179 @@ g13_init_default_ctrl (struct server_control_s *ctrl)
}
/* static void */
/* daemonize (int nodetach) */
/* { */
/* gnupg_fd_t fd; */
/* gnupg_fd_t fd_ssh; */
/* pid_t pid; */
/* fflush (NULL); */
/* #ifdef HAVE_W32_SYSTEM */
/* pid = getpid (); */
/* #else /\*!HAVE_W32_SYSTEM*\/ */
/* pid = fork (); */
/* if (pid == (pid_t)-1) */
/* { */
/* log_fatal ("fork failed: %s\n", strerror (errno) ); */
/* g13_exit (1); */
/* } */
/* else if (pid) /\* We are the parent *\/ */
/* { */
/* /\* We need to clwanup our resources. An gcry_atfork might be */
/* needed. *\/ */
/* exit (0); */
/* /\*NOTREACHED*\/ */
/* } /\* End parent *\/ */
/* /\* */
/* This is the child */
/* *\/ */
/* /\* Detach from tty and put process into a new session *\/ */
/* if (!nodetach ) */
/* { */
/* int i; */
/* unsigned int oldflags; */
/* This function is called for each signal we catch. It is run in the
main context or the one of a Pth thread and thus it is not
restricted in what it may do. */
static void
handle_signal (int signo)
{
switch (signo)
{
#ifndef HAVE_W32_SYSTEM
case SIGHUP:
log_info ("SIGHUP received - re-reading configuration\n");
/* Fixme: Not yet implemented. */
break;
/* /\* Close stdin, stdout and stderr unless it is the log stream *\/ */
/* for (i=0; i <= 2; i++) */
/* { */
/* if (!log_test_fd (i) && i != fd ) */
/* { */
/* if ( ! close (i) */
/* && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1) */
/* { */
/* log_error ("failed to open `%s': %s\n", */
/* "/dev/null", strerror (errno)); */
/* cleanup (); */
/* exit (1); */
/* } */
/* } */
/* } */
/* if (setsid() == -1) */
/* { */
/* log_error ("setsid() failed: %s\n", strerror(errno) ); */
/* cleanup (); */
/* exit (1); */
/* } */
case SIGUSR1:
log_info ("SIGUSR1 received - printing internal information:\n");
pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ());
break;
/* log_get_prefix (&oldflags); */
/* log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED); */
/* opt.running_detached = 1; */
/* } */
case SIGUSR2:
log_info ("SIGUSR2 received - no action defined\n");
break;
case SIGTERM:
if (!shutdown_pending)
log_info ("SIGTERM received - shutting down ...\n");
else
log_info ("SIGTERM received - still %u runners active\n",
runner_get_threads ());
shutdown_pending++;
if (shutdown_pending > 2)
{
log_info ("shutdown forced\n");
log_info ("%s %s stopped\n", strusage(11), strusage(13) );
g13_exit (0);
}
break;
case SIGINT:
log_info ("SIGINT received - immediate shutdown\n");
log_info( "%s %s stopped\n", strusage(11), strusage(13));
g13_exit (0);
break;
#endif /*!HAVE_W32_SYSTEM*/
default:
log_info ("signal %d received - no action defined\n", signo);
}
}
/* This ticker function is called about every TIMERTICK_INTERVAL_SEC
seconds. */
static void
handle_tick (void)
{
/* log_debug ("TICK\n"); */
}
/* The idle task. We use a separate thread to do idle stuff and to
catch signals. */
static void *
idle_task (void *dummy_arg)
{
sigset_t sigs; /* The set of signals we want to catch. */
pth_event_t ev; /* The main event to catch signals. */
pth_event_t time_ev; /* The time event. */
int signo; /* The number of a raised signal is stored here. */
(void)dummy_arg;
/* Create the event to catch the signals. */
#ifndef HAVE_W32_SYSTEM
sigemptyset (&sigs );
sigaddset (&sigs, SIGHUP);
sigaddset (&sigs, SIGUSR1);
sigaddset (&sigs, SIGUSR2);
sigaddset (&sigs, SIGINT);
sigaddset (&sigs, SIGTERM);
pth_sigmask (SIG_UNBLOCK, &sigs, NULL);
#else
sigs = 0;
#endif
ev = pth_event (PTH_EVENT_SIGS, &sigs, &signo);
/* The time event neds to computed n tghe fly. */
time_ev = NULL;
for (;;)
{
/* The shutdown flag allows us to terminate the idle task. */
if (shutdown_pending)
{
runner_cancel_all ();
if (!runner_get_threads ())
break; /* ready */
}
/* Create a timeout event if needed. To help with power saving
we syncronize the ticks to the next full second. */
if (!time_ev)
{
pth_time_t nexttick;
nexttick = pth_timeout (TIMERTICK_INTERVAL_SEC, 0);
if (nexttick.tv_usec > 10) /* Use a 10 usec threshhold. */
{
nexttick.tv_sec++;
nexttick.tv_usec = 0;
}
time_ev = pth_event (PTH_EVENT_TIME, nexttick);
}
pth_event_concat (ev, time_ev, NULL);
pth_wait (ev);
pth_event_isolate (time_ev);
if (pth_event_occurred (ev))
{
handle_signal (signo);
}
if (time_ev && pth_event_occurred (time_ev))
{
pth_event_free (time_ev, PTH_FREE_ALL);
time_ev = NULL;
handle_tick ();
}
}
pth_event_free (ev, PTH_FREE_ALL);
if (time_ev)
pth_event_free (time_ev, PTH_FREE_ALL);
log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
return NULL;
}
/* Start the idle task. */
static void
start_idle_task (void)
{
pth_attr_t tattr;
pth_t tid;
/* if (chdir("/")) */
/* { */
/* log_error ("chdir to / failed: %s\n", strerror (errno)); */
/* exit (1); */
/* } */
tattr = pth_attr_new ();
pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1);
pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
pth_attr_set (tattr, PTH_ATTR_NAME, "idle-task");
tid = pth_spawn (tattr, idle_task, NULL);
if (!tid)
{
log_fatal ("error starting idle task: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
return; /*NOTREACHED*/
}
idle_task_tid = tid;
pth_attr_destroy (tattr);
}
/* { */
/* struct sigaction sa; */
/* sa.sa_handler = SIG_IGN; */
/* sigemptyset (&sa.sa_mask); */
/* sa.sa_flags = 0; */
/* sigaction (SIGPIPE, &sa, NULL); */
/* } */
/* #endif /\*!HAVE_W32_SYSTEM*\/ */
/* log_info ("%s %s started\n", strusage(11), strusage(13) ); */
/* handle_something (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD); */
/* } */
/* Wait for the idle task to finish. */
static void
join_idle_task (void)
{
if (idle_task_tid)
{
if (!pth_join (idle_task_tid, NULL))
log_error ("waiting for idle task thread failed: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
}
}

View File

@ -30,6 +30,7 @@
#include "../common/status.h"
#include "../common/estream.h"
#include "../common/audit.h"
#include "../common/session-env.h"
/* A large struct named "opt" to keep global flags. */
struct
@ -58,6 +59,7 @@ struct
char *lc_messages;
char *xauthority;
char *pinentry_user_data;
session_env_t session_env;
/* Name of the output file - FIXME: what is this? */
const char *outfile;

View File

@ -40,9 +40,12 @@ struct runner_s
int spawned; /* True if runner_spawn has been called. */
pth_t threadid; /* The TID of the runner thread. */
runner_t next_running; /* Builds a list of all running threads. */
int canceled; /* Set if a cancel has already been send once. */
int cancel_flag; /* If set the thread should terminate itself. */
/* We use a reference counter to know when it is safe to remove the
object. Lackiong an explicit ref fucntion this counter will take
only these two values:
@ -66,8 +69,9 @@ struct runner_s
};
/* Avariabale to track the number of active runner threads. */
static unsigned int thread_count;
/* The head of the list of all running threads. */
static runner_t running_threads;
@ -114,7 +118,12 @@ check_already_spawned (runner_t runner, const char *funcname)
unsigned int
runner_get_threads (void)
{
return thread_count;
unsigned int n = 0;
runner_t r;
for (r = running_threads; r; r = r->next_running)
n++;
return n;
}
@ -240,7 +249,7 @@ static void *
runner_thread (void *arg)
{
runner_t runner = arg;
gpg_error_t err;
gpg_error_t err = 0;
log_debug ("starting runner thread\n");
/* If a status_fp is available, the thread's main task is to read
@ -257,7 +266,6 @@ runner_thread (void *arg)
estream_t fp = runner->status_fp;
pos = 0;
err = 0;
cont_line = 0;
while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF)
{
@ -322,9 +330,22 @@ runner_thread (void *arg)
/* Get rid of the runner object (note: it is refcounted). */
log_debug ("runner thread releasing runner ...\n");
{
runner_t r, rprev;
for (r = running_threads, rprev = NULL; r; rprev = r, r = r->next_running)
if (r == runner)
{
if (!rprev)
running_threads = r->next_running;
else
rprev->next_running = r->next_running;
r->next_running = NULL;
break;
}
}
runner_release (runner);
log_debug ("runner thread runner released\n");
thread_count--;
return NULL;
}
@ -374,14 +395,14 @@ runner_spawn (runner_t runner)
}
/* The scheduler has not yet kicked in, thus we can safely set the
spawned flag and the tid. */
thread_count++;
runner->spawned = 1;
runner->threadid = tid;
runner->next_running = running_threads;
running_threads = runner;
pth_attr_destroy (tattr);
/* The runner thread is now runnable. */
return 0;
}
@ -391,8 +412,10 @@ runner_spawn (runner_t runner)
void
runner_cancel (runner_t runner)
{
/* Warning: runner_cancel_all has knowledge of this code. */
if (runner->spawned)
{
runner->canceled = 1; /* Mark that we canceled this one already. */
/* FIXME: This does only work if the thread emits status lines. We
need to change the trhead to wait on an event. */
runner->cancel_flag = 1;
@ -402,6 +425,25 @@ runner_cancel (runner_t runner)
}
/* Cancel all runner threads. */
void
runner_cancel_all (void)
{
runner_t r;
do
{
for (r = running_threads; r; r = r->next_running)
if (r->spawned && !r->canceled)
{
runner_cancel (r);
break;
}
}
while (r);
}
/* Send a line of data down to the engine. This line may not contain
a binary Nul or a LF character. This function is used by the
engine's handler. */

View File

@ -57,6 +57,9 @@ gpg_error_t runner_spawn (runner_t runner);
/* Cancel a runner. */
void runner_cancel (runner_t runner);
/* Cancel all runner. */
void runner_cancel_all (void);
/* Send data back to the engine. This function is used by the
engine's handler. */
gpg_error_t runner_send_line (runner_t runner,

709
g13/server.c Normal file
View File

@ -0,0 +1,709 @@
/* server.c - The G13 Assuan server
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* 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 <http://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.h"
#include <assuan.h>
#include "i18n.h"
#include "keyblob.h"
#include "./server.h"
#include "./mount.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 contect we are working on. */
assuan_context_t assuan_ctx;
char *mountpoint; /* Malloced current mountpoint. */
};
/* Cookie definition for assuan data line output. */
static ssize_t data_line_cookie_write (void *cookie,
const void *buffer, size_t size);
static int data_line_cookie_close (void *cookie);
static es_cookie_io_functions_t data_line_cookie_functions =
{
NULL,
data_line_cookie_write,
NULL,
data_line_cookie_close
};
/* The filepointer for status message used in non-server mode. */
/* static FILE *statusfp; FIXME; */
static int command_has_option (const char *cmd, const char *cmdopt);
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
/* 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)));
}
/* A write handler used by es_fopencookie to write Assuan data
lines. */
static ssize_t
data_line_cookie_write (void *cookie, const void *buffer, size_t size)
{
assuan_context_t ctx = cookie;
if (assuan_send_data (ctx, buffer, size))
{
errno = EIO;
return -1;
}
return size;
}
/* A close handler used by es_fopencookie to write Assuan data
lines. */
static int
data_line_cookie_close (void *cookie)
{
assuan_context_t ctx = cookie;
if (assuan_send_data (ctx, NULL, 0))
{
errno = EIO;
return -1;
}
return 0;
}
/* 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;
if (!strcmp (key, "putenv"))
{
/* Change the session's environment to be used for the
Pinentry. Valid values are:
<NAME> Delete envvar NAME
<KEY>= Set envvar NAME to the empty string
<KEY>=<VALUE> Set envvar NAME to VALUE
*/
err = session_env_putenv (opt.session_env, value);
}
else if (!strcmp (key, "display"))
{
err = session_env_setenv (opt.session_env, "DISPLAY", value);
}
else if (!strcmp (key, "ttyname"))
{
err = session_env_setenv (opt.session_env, "GPG_TTY", value);
}
else if (!strcmp (key, "ttytype"))
{
err = session_env_setenv (opt.session_env, "TERM", value);
}
else if (!strcmp (key, "lc-ctype"))
{
xfree (opt.lc_ctype);
opt.lc_ctype = xtrystrdup (value);
if (!opt.lc_ctype)
err = gpg_error_from_syserror ();
}
else if (!strcmp (key, "lc-messages"))
{
xfree (opt.lc_messages);
opt.lc_messages = xtrystrdup (value);
if (!opt.lc_messages)
err = gpg_error_from_syserror ();
}
else if (!strcmp (key, "xauthority"))
{
err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
}
else if (!strcmp (key, "pinentry-user-data"))
{
err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
}
else if (!strcmp (key, "enable-audit-log"))
{
/* This is not yet used. */
/* int i = *value? atoi (value) : 0; */
/* ctrl->server_local->enable_audit_log = i; */
}
else if (!strcmp (key, "allow-pinentry-notify"))
{
; /* We always allow it. */
}
else
err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
return err;
}
/* The handler for an Assuan RESET command. */
static void
reset_notify (assuan_context_t ctx)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
xfree (ctrl->server_local->mountpoint);
ctrl->server_local->mountpoint = NULL;
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
}
/* 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;
}
/* RECIPIENT <userID>
FIXME - description.
All RECIPIENT commands are cumulative until a RESET or an
successful CREATE command.
*/
static gpg_error_t
cmd_recipient (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
(void)ctrl;
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
/* err = gpgsm_add_to_certlist (ctrl, line, 0, */
/* &ctrl->server_local->recplist, 0); */
/* if (err) */
/* { */
/* gpgsm_status2 (ctrl, STATUS_INV_RECP, */
/* get_inv_recpsgnr_code (rc), line, NULL); */
/* } */
return leave_cmd (ctx, err);
}
/* SIGNER <userID>
FIXME - description.
*/
static gpg_error_t
cmd_signer (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
(void)ctrl;
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
return leave_cmd (ctx, err);
}
/* SETMOUNTPOINT [options] [<dirname>]
Set DIRNAME as the new mount point for future operations.
*/
static gpg_error_t
cmd_setmountpoint (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
char *p, *pend;
size_t len;
line = skip_options (line);
for (p=line; *p && !spacep (p); p++)
;
pend = p;
while (spacep(p))
p++;
if (*p)
{
err = gpg_error (GPG_ERR_ASS_SYNTAX);
goto leave;
}
*pend = 0;
/* Unescape the line and check for embedded Nul bytes. */
len = percent_plus_unescape_inplace (line, 0);
line[len] = 0;
if (memchr (line, 0, len))
{
err = gpg_error (GPG_ERR_INV_NAME);
goto leave;
}
xfree (ctrl->server_local->mountpoint);
if (!len) /* Reset mountpoint. */
ctrl->server_local->mountpoint = NULL;
else
{
ctrl->server_local->mountpoint = xtrystrdup (line);
if (!ctrl->server_local->mountpoint)
err = gpg_error_from_syserror ();
}
if (!err)
log_debug ("mountpoint is now `%s'\n",
ctrl->server_local->mountpoint
? ctrl->server_local->mountpoint: "[none]");
leave:
return leave_cmd (ctx, err);
}
/* MOUNT [options] <containername>
Mount CONTAINERNAME onto the current mount point. CONTAINERNAME is
the name of a file in the g13 format and must be percent-plus
escaped to allow for arbitrary names. The mount poiunt must have
been set already.
A reason why we use a separate command for the mount point is to
allow for longer filenames (an assuan command line is limited to
~1000 byte.
*/
static gpg_error_t
cmd_mount (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
char *p, *pend;
size_t len;
line = skip_options (line);
for (p=line; *p && !spacep (p); p++)
;
pend = p;
while (spacep(p))
p++;
if (*p || pend == line)
{
err = gpg_error (GPG_ERR_ASS_SYNTAX);
goto leave;
}
*pend = 0;
/* Unescape the line and check for embedded Nul bytes. */
len = percent_plus_unescape_inplace (line, 0);
line[len] = 0;
if (!len || memchr (line, 0, len))
{
err = gpg_error (GPG_ERR_INV_NAME);
goto leave;
}
/* Perform the mount. */
err = g13_mount_container (ctrl, line, ctrl->server_local->mountpoint);
leave:
return leave_cmd (ctx, err);
}
/* GETINFO <what>
Multipurpose function to return a variety of information.
Supported values for WHAT are:
version - Return the version of the program.
pid - Return the process id of the server.
cmd_has_option CMD OPT
- Returns OK if the command CMD implements the option OPT.
*/
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
gpg_error_t err = 0;
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, "cmd_has_option", 14)
&& (line[14] == ' ' || line[14] == '\t' || !line[14]))
{
char *cmd, *cmdopt;
line += 14;
while (*line == ' ' || *line == '\t')
line++;
if (!*line)
err = gpg_error (GPG_ERR_MISSING_VALUE);
else
{
cmd = line;
while (*line && (*line != ' ' && *line != '\t'))
line++;
if (!*line)
err = gpg_error (GPG_ERR_MISSING_VALUE);
else
{
*line++ = 0;
while (*line == ' ' || *line == '\t')
line++;
if (!*line)
err = gpg_error (GPG_ERR_MISSING_VALUE);
else
{
cmdopt = line;
if (!command_has_option (cmd, cmdopt))
err = gpg_error (GPG_ERR_GENERAL);
}
}
}
}
else
err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
return leave_cmd (ctx, err);
}
/* Return true if the command CMD implements the option CMDOPT. */
static int
command_has_option (const char *cmd, const char *cmdopt)
{
(void)cmd;
(void)cmdopt;
return 0;
}
/* Tell the Assuan library about our commands. */
static int
register_commands (assuan_context_t ctx)
{
static struct {
const char *name;
gpg_error_t (*handler)(assuan_context_t, char *line);
} table[] = {
{ "RECIPIENT", cmd_recipient },
{ "SIGNER", cmd_signer },
{ "MOUNT", cmd_mount },
{ "SETMOUNTPOINT", cmd_setmountpoint },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "GETINFO", cmd_getinfo },
{ NULL }
};
gpg_error_t err;
int i;
for (i=0; table[i].name; i++)
{
err = assuan_register_command (ctx, table[i].name, table[i].handler);
if (err)
return err;
}
return 0;
}
/* Startup the server. DEFAULT_RECPLIST is the list of recipients as
set from the command line or config file. We only require those
marked as encrypt-to. */
gpg_error_t
g13_server (ctrl_t ctrl)
{
gpg_error_t err;
int filedes[2];
assuan_context_t ctx = NULL;
static const char hello[] = ("GNU Privacy Guard's G13 server "
PACKAGE_VERSION " ready");
/* 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 FIELDES in this case. */
filedes[0] = 0;
filedes[1] = 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);
if (err)
{
log_error ("failed to the register commands with Assuan: %s\n",
gpg_strerror (err));
goto leave;
}
assuan_set_pointer (ctx, ctrl);
if (opt.verbose || opt.debug)
{
char *tmp = NULL;
const char *s1 = getenv ("GPG_AGENT_INFO");
tmp = xtryasprintf ("Home: %s\n"
"Config: %s\n"
"AgentInfo: %s\n"
"%s",
opt.homedir,
opt.config_filename,
s1?s1:"[not set]",
hello);
if (tmp)
{
assuan_set_hello_line (ctx, tmp);
xfree (tmp);
}
}
else
assuan_set_hello_line (ctx, hello);
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;
if (DBG_ASSUAN)
assuan_set_log_stream (ctx, log_get_stream ());
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:
if (ctrl->server_local)
{
xfree (ctrl->server_local);
ctrl->server_local = NULL;
}
assuan_release (ctx);
return err;
}
/* gpg_error_t */
/* gpgsm_status2 (ctrl_t ctrl, int no, ...) */
/* { */
/* gpg_error_t err = 0; */
/* va_list arg_ptr; */
/* const char *text; */
/* va_start (arg_ptr, no); */
/* if (ctrl->no_server && ctrl->status_fd == -1) */
/* ; /\* No status wanted. *\/ */
/* else if (ctrl->no_server) */
/* { */
/* if (!statusfp) */
/* { */
/* if (ctrl->status_fd == 1) */
/* statusfp = stdout; */
/* else if (ctrl->status_fd == 2) */
/* statusfp = stderr; */
/* else */
/* statusfp = fdopen (ctrl->status_fd, "w"); */
/* if (!statusfp) */
/* { */
/* log_fatal ("can't open fd %d for status output: %s\n", */
/* ctrl->status_fd, strerror(errno)); */
/* } */
/* } */
/* fputs ("[GNUPG:] ", statusfp); */
/* fputs (get_status_string (no), statusfp); */
/* while ( (text = va_arg (arg_ptr, const char*) )) */
/* { */
/* putc ( ' ', statusfp ); */
/* for (; *text; text++) */
/* { */
/* if (*text == '\n') */
/* fputs ( "\\n", statusfp ); */
/* else if (*text == '\r') */
/* fputs ( "\\r", statusfp ); */
/* else */
/* putc ( *(const byte *)text, statusfp ); */
/* } */
/* } */
/* putc ('\n', statusfp); */
/* fflush (statusfp); */
/* } */
/* else */
/* { */
/* assuan_context_t ctx = ctrl->server_local->assuan_ctx; */
/* char buf[950], *p; */
/* size_t n; */
/* p = buf; */
/* n = 0; */
/* while ( (text = va_arg (arg_ptr, const char *)) ) */
/* { */
/* if (n) */
/* { */
/* *p++ = ' '; */
/* n++; */
/* } */
/* for ( ; *text && n < DIM (buf)-2; n++) */
/* *p++ = *text++; */
/* } */
/* *p = 0; */
/* err = assuan_write_status (ctx, get_status_string (no), buf); */
/* } */
/* va_end (arg_ptr); */
/* return err; */
/* } */
/* gpg_error_t */
/* gpgsm_status (ctrl_t ctrl, int no, const char *text) */
/* { */
/* return gpgsm_status2 (ctrl, no, text, NULL); */
/* } */
/* gpg_error_t */
/* gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, */
/* gpg_err_code_t ec) */
/* { */
/* char buf[30]; */
/* sprintf (buf, "%u", (unsigned int)ec); */
/* if (text) */
/* return gpgsm_status2 (ctrl, no, text, buf, NULL); */
/* else */
/* return gpgsm_status2 (ctrl, no, buf, NULL); */
/* } */
/* Helper to notify the client about Pinentry events. Returns an gpg
error code. */
gpg_error_t
g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
{
if (!ctrl || !ctrl->server_local)
return 0;
return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
}

29
g13/server.h Normal file
View File

@ -0,0 +1,29 @@
/* server.h - The G13 Assuan server
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef G13_SERVER_H
#define G13_SERVER_H
gpg_error_t g13_server (ctrl_t ctrl);
gpg_error_t g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line);
#endif /*G13_SERVER_H*/