From 1445c15ed1d874ad624bb2a635b48d82a2b9d9d9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 14 Oct 2009 17:06:10 +0000 Subject: [PATCH] mount does now work in server and standalone mode. Implemented a signal handler. --- g13/Makefile.am | 1 + g13/be-encfs.c | 3 +- g13/create.c | 2 +- g13/g13.c | 314 ++++++++++++++------- g13/g13.h | 2 + g13/runner.c | 60 +++- g13/runner.h | 3 + g13/server.c | 709 ++++++++++++++++++++++++++++++++++++++++++++++++ g13/server.h | 29 ++ 9 files changed, 1011 insertions(+), 112 deletions(-) create mode 100644 g13/server.c create mode 100644 g13/server.h diff --git a/g13/Makefile.am b/g13/Makefile.am index 2108f562a..e65684c0f 100644 --- a/g13/Makefile.am +++ b/g13/Makefile.am @@ -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 \ diff --git a/g13/be-encfs.c b/g13/be-encfs.c index 0f7ec73e6..de3209a91 100644 --- a/g13/be-encfs.c +++ b/g13/be-encfs.c @@ -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; diff --git a/g13/create.c b/g13/create.c index 7f3a349b2..0279c337d 100644 --- a/g13/create.c +++ b/g13/create.c @@ -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. diff --git a/g13/g13.c b/g13/g13.c index 6b4d05a7f..288689457 100644 --- a/g13/g13.c +++ b/g13/g13.c @@ -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 ())); + } +} + diff --git a/g13/g13.h b/g13/g13.h index 5740e5860..0bfc42e9c 100644 --- a/g13/g13.h +++ b/g13/g13.h @@ -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; diff --git a/g13/runner.c b/g13/runner.c index d88b69b98..27d03702a 100644 --- a/g13/runner.c +++ b/g13/runner.c @@ -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. */ diff --git a/g13/runner.h b/g13/runner.h index 0152f22e4..88ebc7d2d 100644 --- a/g13/runner.h +++ b/g13/runner.h @@ -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, diff --git a/g13/server.c b/g13/server.c new file mode 100644 index 000000000..bdf214f32 --- /dev/null +++ b/g13/server.c @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "g13.h" +#include +#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: + Delete envvar NAME + = Set envvar NAME to the empty string + = 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 + + 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 + + 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] [] + + 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] + + 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 + + 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); +} + + + + diff --git a/g13/server.h b/g13/server.h new file mode 100644 index 000000000..9b6eb98d5 --- /dev/null +++ b/g13/server.h @@ -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 . + */ + +#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*/ +