diff options
author | Werner Koch <wk@gnupg.org> | 2009-10-14 19:06:10 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2009-10-14 19:06:10 +0200 |
commit | 1445c15ed1d874ad624bb2a635b48d82a2b9d9d9 (patch) | |
tree | 0f34970d0c1bce67099fe76a3cad9e13fd8b052c /g13/server.c | |
parent | Keep on hacking on g13. A simple --create and --mount does now work. (diff) | |
download | gnupg2-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.c | 709 |
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); +} + + + + |