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 \ g13.c g13.h \
keyblob.h \ keyblob.h \
utils.c utils.h \ utils.c utils.h \
server.c server.h \
create.c create.h \ create.c create.h \
mount.c mount.h \ mount.c mount.h \
call-gpg.c call-gpg.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"; pgmname = "/usr/bin/encfs";
idx = 0; idx = 0;
argv[idx++] = "-f"; argv[idx++] = "-f";
argv[idx++] = "-v"; if (opt.verbose)
argv[idx++] = "-v";
argv[idx++] = "--stdinpass"; argv[idx++] = "--stdinpass";
argv[idx++] = "--annotate"; argv[idx++] = "--annotate";
argv[idx++] = rawdir; argv[idx++] = rawdir;

View File

@ -39,7 +39,7 @@
/* Create a new blob with all the session keys and other meta /* Create a new blob with all the session keys and other meta
information which are to be stored encrypted in the crypto information which are to be stored encrypted in the crypto
container header. On success the malloced blob is stored at R_BLOB 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). and (R_BLOB,R_BLOBLEN) are set to (NULL,0).
The format of this blob is a sequence of tag-length-value tuples. 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 "sysutils.h"
#include "gc-opt-flags.h" #include "gc-opt-flags.h"
#include "keyblob.h" #include "keyblob.h"
#include "./runner.h" #include "server.h"
#include "./create.h" #include "runner.h"
#include "create.h"
#include "./mount.h" #include "./mount.h"
@ -51,6 +52,7 @@ enum cmd_and_opt_values {
aCreate, aCreate,
aMount, aMount,
aUmount, aUmount,
aServer,
oOptions, oOptions,
oDebug, oDebug,
@ -102,6 +104,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aCreate, "create", N_("Create a new file system container")), ARGPARSE_c (aCreate, "create", N_("Create a new file system container")),
ARGPARSE_c (aMount, "mount", N_("Mount a 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 (aUmount, "umount", N_("Unmount a file system container") ),
ARGPARSE_c (aServer, "server", N_("Run in server mode")),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), 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. */ /* Global variable to keep an error count. */
int g13_errors_seen = 0; int g13_errors_seen = 0;
@ -178,11 +185,23 @@ static int maybe_setuid = 1;
static const char *debug_level; static const char *debug_level;
static unsigned int debug_value; 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, static void set_cmd (enum cmd_and_opt_values *ret_cmd,
enum cmd_and_opt_values new_cmd ); enum cmd_and_opt_values new_cmd );
static void emergency_cleanup (void); static void emergency_cleanup (void);
static void start_idle_task (void);
static void join_idle_task (void);
/* Begin Pth wrapper functions. */ /* Begin Pth wrapper functions. */
GCRY_THREAD_OPTION_PTH_IMPL; GCRY_THREAD_OPTION_PTH_IMPL;
static int fixed_gcry_pth_init (void) static int fixed_gcry_pth_init (void)
@ -475,6 +494,7 @@ main ( int argc, char **argv)
nokeysetup = 1; nokeysetup = 1;
break; break;
case aServer:
case aMount: case aMount:
case aUmount: case aUmount:
nokeysetup = 1; nokeysetup = 1;
@ -591,9 +611,9 @@ main ( int argc, char **argv)
if (greeting) if (greeting)
{ {
fprintf(stderr, "%s %s; %s\n", fprintf (stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) ); strusage(11), strusage(13), strusage(14) );
fprintf(stderr, "%s\n", strusage(15) ); fprintf (stderr, "%s\n", strusage(15) );
} }
if (may_coredump && !opt.quiet) if (may_coredump && !opt.quiet)
@ -680,24 +700,25 @@ main ( int argc, char **argv)
configuration file is valid. */ configuration file is valid. */
break; 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. */ case aCreate: /* Create a new container. */
{ {
if (argc != 1) if (argc != 1)
wrong_args ("--create filename"); wrong_args ("--create filename");
start_idle_task ();
err = g13_create_container (&ctrl, argv[0]); err = g13_create_container (&ctrl, argv[0]);
if (err) if (err)
log_error ("error creating a new container: %s <%s>\n", log_error ("error creating a new container: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err)); 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; break;
@ -705,20 +726,11 @@ main ( int argc, char **argv)
{ {
if (argc != 1 && argc != 2 ) if (argc != 1 && argc != 2 )
wrong_args ("--mount filename [mountpoint]"); wrong_args ("--mount filename [mountpoint]");
start_idle_task ();
err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL); err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL);
if (err) if (err)
log_error ("error mounting container `%s': %s <%s>\n", log_error ("error mounting container `%s': %s <%s>\n",
*argv, gpg_strerror (err), gpg_strsource (err)); *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; break;
@ -727,6 +739,9 @@ main ( int argc, char **argv)
break; break;
} }
if (!err)
join_idle_task ();
/* Print the audit result if needed. */ /* Print the audit result if needed. */
if (auditlog && auditfp) if (auditlog && auditfp)
{ {
@ -741,6 +756,7 @@ main ( int argc, char **argv)
return 8; /*NOTREACHED*/ return 8; /*NOTREACHED*/
} }
/* Note: This function is used by signal handlers!. */ /* Note: This function is used by signal handlers!. */
static void static void
emergency_cleanup (void) emergency_cleanup (void)
@ -766,6 +782,7 @@ g13_exit (int rc)
} }
/* Store defaults into the per-connection CTRL object. */
void void
g13_init_default_ctrl (struct server_control_s *ctrl) g13_init_default_ctrl (struct server_control_s *ctrl)
{ {
@ -773,84 +790,179 @@ g13_init_default_ctrl (struct server_control_s *ctrl)
} }
/* static void */ /* This function is called for each signal we catch. It is run in the
/* daemonize (int nodetach) */ main context or the one of a Pth thread and thus it is not
/* { */ restricted in what it may do. */
/* gnupg_fd_t fd; */ static void
/* gnupg_fd_t fd_ssh; */ handle_signal (int signo)
/* pid_t pid; */ {
switch (signo)
/* fflush (NULL); */ {
/* #ifdef HAVE_W32_SYSTEM */ #ifndef HAVE_W32_SYSTEM
/* pid = getpid (); */ case SIGHUP:
/* #else /\*!HAVE_W32_SYSTEM*\/ */ log_info ("SIGHUP received - re-reading configuration\n");
/* pid = fork (); */ /* Fixme: Not yet implemented. */
/* if (pid == (pid_t)-1) */ break;
/* { */
/* 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; */
/* /\* Close stdin, stdout and stderr unless it is the log stream *\/ */ case SIGUSR1:
/* for (i=0; i <= 2; i++) */ log_info ("SIGUSR1 received - printing internal information:\n");
/* { */ pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ());
/* if (!log_test_fd (i) && i != fd ) */ break;
/* { */
/* 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); */
/* } */
/* log_get_prefix (&oldflags); */ case SIGUSR2:
/* log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED); */ log_info ("SIGUSR2 received - no action defined\n");
/* opt.running_detached = 1; */ 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("/")) */ tattr = pth_attr_new ();
/* { */ pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1);
/* log_error ("chdir to / failed: %s\n", strerror (errno)); */ pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024);
/* exit (1); */ 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) ); */ /* Wait for the idle task to finish. */
/* handle_something (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD); */ 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/status.h"
#include "../common/estream.h" #include "../common/estream.h"
#include "../common/audit.h" #include "../common/audit.h"
#include "../common/session-env.h"
/* A large struct named "opt" to keep global flags. */ /* A large struct named "opt" to keep global flags. */
struct struct
@ -58,6 +59,7 @@ struct
char *lc_messages; char *lc_messages;
char *xauthority; char *xauthority;
char *pinentry_user_data; char *pinentry_user_data;
session_env_t session_env;
/* Name of the output file - FIXME: what is this? */ /* Name of the output file - FIXME: what is this? */
const char *outfile; const char *outfile;

View File

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

View File

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