summaryrefslogtreecommitdiffstats
path: root/g13/server.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2009-10-14 19:06:10 +0200
committerWerner Koch <wk@gnupg.org>2009-10-14 19:06:10 +0200
commit1445c15ed1d874ad624bb2a635b48d82a2b9d9d9 (patch)
tree0f34970d0c1bce67099fe76a3cad9e13fd8b052c /g13/server.c
parentKeep on hacking on g13. A simple --create and --mount does now work. (diff)
downloadgnupg2-1445c15ed1d874ad624bb2a635b48d82a2b9d9d9.tar.xz
gnupg2-1445c15ed1d874ad624bb2a635b48d82a2b9d9d9.zip
mount does now work in server and standalone mode.
Implemented a signal handler.
Diffstat (limited to 'g13/server.c')
-rw-r--r--g13/server.c709
1 files changed, 709 insertions, 0 deletions
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 <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);
+}
+
+
+
+