aboutsummaryrefslogtreecommitdiffstats
path: root/sm/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'sm/server.c')
-rw-r--r--sm/server.c1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/sm/server.c b/sm/server.c
new file mode 100644
index 000000000..dda150964
--- /dev/null
+++ b/sm/server.c
@@ -0,0 +1,1070 @@
+/* server.c - Server mode and main entry point
+ * Copyright (C) 2001, 2002, 2003 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <assuan.h>
+
+#include "gpgsm.h"
+
+#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
+
+
+/* The filepointer for status message used in non-server mode */
+static FILE *statusfp;
+
+/* Data used to assuciate an Assuan context with local server data */
+struct server_local_s {
+ ASSUAN_CONTEXT assuan_ctx;
+ int message_fd;
+ int list_internal;
+ int list_external;
+ CERTLIST recplist;
+ CERTLIST signerlist;
+};
+
+
+
+/* note, that it is sufficient to allocate the target string D as
+ long as the source string S, i.e.: strlen(s)+1; */
+static void
+strcpy_escaped_plus (char *d, const unsigned char *s)
+{
+ while (*s)
+ {
+ if (*s == '%' && s[1] && s[2])
+ {
+ s++;
+ *d++ = xtoi_2 ( s);
+ s += 2;
+ }
+ else if (*s == '+')
+ *d++ = ' ', s++;
+ else
+ *d++ = *s++;
+ }
+ *d = 0;
+}
+
+
+
+
+/* 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);
+ return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+
+static void
+close_message_fd (CTRL ctrl)
+{
+ if (ctrl->server_local->message_fd != -1)
+ {
+ close (ctrl->server_local->message_fd);
+ ctrl->server_local->message_fd = -1;
+ }
+}
+
+
+static int
+option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ if (!strcmp (key, "include-certs"))
+ {
+ int i = *value? atoi (value) : -1;
+ if (ctrl->include_certs < -2)
+ return ASSUAN_Parameter_Error;
+ ctrl->include_certs = i;
+ }
+ else if (!strcmp (key, "display"))
+ {
+ if (opt.display)
+ free (opt.display);
+ opt.display = strdup (value);
+ if (!opt.display)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "ttyname"))
+ {
+ if (opt.ttyname)
+ free (opt.ttyname);
+ opt.ttyname = strdup (value);
+ if (!opt.ttyname)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "ttytype"))
+ {
+ if (opt.ttytype)
+ free (opt.ttytype);
+ opt.ttytype = strdup (value);
+ if (!opt.ttytype)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "lc-ctype"))
+ {
+ if (opt.lc_ctype)
+ free (opt.lc_ctype);
+ opt.lc_ctype = strdup (value);
+ if (!opt.lc_ctype)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "lc-messages"))
+ {
+ if (opt.lc_messages)
+ free (opt.lc_messages);
+ opt.lc_messages = strdup (value);
+ if (!opt.lc_messages)
+ return ASSUAN_Out_Of_Core;
+ }
+ else if (!strcmp (key, "list-mode"))
+ {
+ int i = *value? atoi (value) : 0;
+ if (!i || i == 1) /* default and mode 1 */
+ {
+ ctrl->server_local->list_internal = 1;
+ ctrl->server_local->list_external = 0;
+ }
+ else if (i == 2)
+ {
+ ctrl->server_local->list_internal = 0;
+ ctrl->server_local->list_external = 1;
+ }
+ else if (i == 3)
+ {
+ ctrl->server_local->list_internal = 1;
+ ctrl->server_local->list_external = 1;
+ }
+ else
+ return ASSUAN_Parameter_Error;
+ }
+ else
+ return ASSUAN_Invalid_Option;
+
+ return 0;
+}
+
+
+
+
+static void
+reset_notify (ASSUAN_CONTEXT ctx)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ gpgsm_release_certlist (ctrl->server_local->recplist);
+ gpgsm_release_certlist (ctrl->server_local->signerlist);
+ ctrl->server_local->recplist = NULL;
+ ctrl->server_local->signerlist = NULL;
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+}
+
+
+static void
+input_notify (ASSUAN_CONTEXT ctx, const char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ ctrl->autodetect_encoding = 0;
+ ctrl->is_pem = 0;
+ ctrl->is_base64 = 0;
+ if (strstr (line, "--armor"))
+ ctrl->is_pem = 1;
+ else if (strstr (line, "--base64"))
+ ctrl->is_base64 = 1;
+ else if (strstr (line, "--binary"))
+ ;
+ else
+ ctrl->autodetect_encoding = 1;
+}
+
+static void
+output_notify (ASSUAN_CONTEXT ctx, const char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ ctrl->create_pem = 0;
+ ctrl->create_base64 = 0;
+ if (strstr (line, "--armor"))
+ ctrl->create_pem = 1;
+ else if (strstr (line, "--base64"))
+ ctrl->create_base64 = 1; /* just the raw output */
+}
+
+
+
+/* RECIPIENT <userID>
+
+ Set the recipient for the encryption. <userID> should be the
+ internal representation of the key; the server may accept any other
+ way of specification [we will support this]. If this is a valid and
+ trusted recipient the server does respond with OK, otherwise the
+ return is an ERR with the reason why the recipient can't be used,
+ the encryption will then not be done for this recipient. IF the
+ policy is not to encrypt at all if not all recipients are valid, the
+ client has to take care of this. All RECIPIENT commands are
+ cumulative until a RESET or an successful ENCRYPT command. */
+static int
+cmd_recipient (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+
+ rc = gpgsm_add_to_certlist (ctrl, line, 0, &ctrl->server_local->recplist);
+ if (rc)
+ {
+ gpg_err_code_t r = gpg_err_code (rc);
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ r == -1? "1":
+ r == GPG_ERR_NO_PUBKEY? "1":
+ r == GPG_ERR_AMBIGUOUS_NAME? "2":
+ r == GPG_ERR_WRONG_KEY_USAGE? "3":
+ r == GPG_ERR_CERT_REVOKED? "4":
+ r == GPG_ERR_CERT_EXPIRED? "5":
+ r == GPG_ERR_NO_CRL_KNOWN? "6":
+ r == GPG_ERR_CRL_TOO_OLD? "7":
+ r == GPG_ERR_NO_POLICY_MATCH? "8":
+ "0",
+ line, NULL);
+ }
+
+ return map_to_assuan_status (rc);
+}
+
+/* SIGNER <userID>
+
+ Set the signer's keys for the signature creation. <userID> should
+ be the internal representation of the key; the server may accept any
+ other way of specification [we will support this]. If this is a
+ valid and usable signing key the server does respond with OK,
+ otherwise it returns an ERR with the reason why the key can't be
+ used, the signing will then not be done for this key. If the policy
+ is not to sign at all if not all signer keys are valid, the client
+ has to take care of this. All SIGNER commands are cumulative until
+ a RESET but they are *not* reset by an SIGN command becuase it can
+ be expected that set of signers are used for more than one sign
+ operation.
+
+ Note that this command returns an INV_RECP status which is a bit
+ strange, but they are very similar. */
+static int
+cmd_signer (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+
+ rc = gpgsm_add_to_certlist (ctrl, line, 1, &ctrl->server_local->signerlist);
+ if (rc)
+ {
+ gpg_err_code_t r = gpg_err_code (rc);
+ gpgsm_status2 (ctrl, STATUS_INV_RECP,
+ r == -1? "1":
+ r == GPG_ERR_NO_PUBKEY? "1":
+ r == GPG_ERR_AMBIGUOUS_NAME? "2":
+ r == GPG_ERR_WRONG_KEY_USAGE? "3":
+ r == GPG_ERR_CERT_REVOKED? "4":
+ r == GPG_ERR_CERT_EXPIRED? "5":
+ r == GPG_ERR_NO_CRL_KNOWN? "6":
+ r == GPG_ERR_CRL_TOO_OLD? "7":
+ r == GPG_ERR_NO_POLICY_MATCH? "8":
+ r == GPG_ERR_NO_SECKEY? "9":
+ "0",
+ line, NULL);
+ }
+ return map_to_assuan_status (rc);
+}
+
+
+/* ENCRYPT
+
+ Do the actual encryption process. Takes the plaintext from the INPUT
+ command, writes to the ciphertext to the file descriptor set with
+ the OUTPUT command, take the recipients form all the recipients set
+ so far. If this command fails the clients should try to delete all
+ output currently done or otherwise mark it as invalid. GPGSM does
+ ensure that there won't be any security problem with leftover data
+ on the output in this case.
+
+ This command should in general not fail, as all necessary checks
+ have been done while setting the recipients. The input and output
+ pipes are closed. */
+static int
+cmd_encrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ rc = gpgsm_encrypt (assuan_get_pointer (ctx),
+ ctrl->server_local->recplist,
+ inp_fd, out_fp);
+ fclose (out_fp);
+
+ gpgsm_release_certlist (ctrl->server_local->recplist);
+ ctrl->server_local->recplist = NULL;
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return map_to_assuan_status (rc);
+}
+
+/* DECRYPT
+
+ This performs the decrypt operation after doing some check on the
+ internal state. (e.g. that only needed data has been set). Because
+ it utilizes the GPG-Agent for the session key decryption, there is
+ no need to ask the client for a protecting passphrase - GpgAgent
+ does take care of this by requesting this from the user. */
+static int
+cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ rc = gpgsm_decrypt (ctrl, inp_fd, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* VERIFY
+
+ This does a verify operation on the message send to the input-FD.
+ The result is written out using status lines. If an output FD was
+ given, the signed text will be written to that.
+
+ If the signature is a detached one, the server will inquire about
+ the signed material and the client must provide it.
+ */
+static int
+cmd_verify (ASSUAN_CONTEXT ctx, char *line)
+{
+ int rc;
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int fd = assuan_get_input_fd (ctx);
+ int out_fd = assuan_get_output_fd (ctx);
+ FILE *out_fp = NULL;
+
+ if (fd == -1)
+ return set_error (No_Input, NULL);
+
+ if (out_fd != -1)
+ {
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ }
+
+ rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
+ ctrl->server_local->message_fd, out_fp);
+ if (out_fp)
+ fclose (out_fp);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* SIGN [--detached]
+
+ Sign the data set with the INPUT command and write it to the sink
+ set by OUTPUT. With "--detached" specified, a detached signature is
+ created (surprise). */
+static int
+cmd_sign (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int detached;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ detached = has_option (line, "--detached");
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+
+ rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
+ inp_fd, detached, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+/* IMPORT
+
+ Import the certificates read form the input-fd, return status
+ message for each imported one. The import checks the validity of
+ the certificate but not of the entire chain. It is possible to
+ import expired certificates. */
+static int
+cmd_import (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ int fd = assuan_get_input_fd (ctx);
+
+ if (fd == -1)
+ return set_error (No_Input, NULL);
+
+ rc = gpgsm_import (assuan_get_pointer (ctx), fd);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+static int
+cmd_export (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int fd = assuan_get_output_fd (ctx);
+ FILE *out_fp;
+ char *p;
+ STRLIST list, sl;
+
+ if (fd == -1)
+ return set_error (No_Output, NULL);
+
+ /* break the line down into an STRLIST */
+ list = NULL;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ sl = xtrymalloc (sizeof *sl + strlen (line));
+ if (!sl)
+ {
+ free_strlist (list);
+ return ASSUAN_Out_Of_Core;
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ out_fp = fdopen ( dup(fd), "w");
+ if (!out_fp)
+ {
+ free_strlist (list);
+ return set_error (General_Error, "fdopen() failed");
+ }
+
+ gpgsm_export (ctrl, list, out_fp);
+ fclose (out_fp);
+ free_strlist (list);
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+ return 0;
+}
+
+
+static int
+cmd_delkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ char *p;
+ STRLIST list, sl;
+ int rc;
+
+ /* break the line down into an STRLIST */
+ list = NULL;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ sl = xtrymalloc (sizeof *sl + strlen (line));
+ if (!sl)
+ {
+ free_strlist (list);
+ return ASSUAN_Out_Of_Core;
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ rc = gpgsm_delete (ctrl, list);
+ free_strlist (list);
+
+ /* close and reset the fd */
+ close_message_fd (ctrl);
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+
+/* MESSAGE FD=<n>
+
+ Set the file descriptor to read a message which is used with
+ detached signatures */
+static int
+cmd_message (ASSUAN_CONTEXT ctx, char *line)
+{
+ char *endp;
+ int fd;
+ CTRL ctrl = assuan_get_pointer (ctx);
+
+ if (strncmp (line, "FD=", 3))
+ return set_error (Syntax_Error, "FD=<n> expected");
+ line += 3;
+ if (!digitp (line))
+ return set_error (Syntax_Error, "number required");
+ fd = strtoul (line, &endp, 10);
+ if (*endp)
+ return set_error (Syntax_Error, "garbage found");
+ if (fd == -1)
+ return set_error (No_Input, NULL);
+
+ ctrl->server_local->message_fd = fd;
+ return 0;
+}
+
+
+static int
+do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ FILE *fp = assuan_get_data_fp (ctx);
+ char *p;
+ STRLIST list, sl;
+ unsigned int listmode;
+
+ if (!fp)
+ return set_error (General_Error, "no data stream");
+
+ /* break the line down into an STRLIST */
+ list = NULL;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ sl = xtrymalloc (sizeof *sl + strlen (line));
+ if (!sl)
+ {
+ free_strlist (list);
+ return ASSUAN_Out_Of_Core;
+ }
+ sl->flags = 0;
+ strcpy_escaped_plus (sl->d, line);
+ sl->next = list;
+ list = sl;
+ }
+ }
+
+ ctrl->with_colons = 1;
+ listmode = mode;
+ if (ctrl->server_local->list_internal)
+ listmode |= (1<<6);
+ if (ctrl->server_local->list_external)
+ listmode |= (1<<7);
+ gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
+ free_strlist (list);
+ return 0;
+}
+
+static int
+cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+ return do_listkeys (ctx, line, 3);
+}
+
+static int
+cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line)
+{
+ return do_listkeys (ctx, line, 2);
+}
+
+
+/* GENKEY
+
+ Read the parameters in native format from the input fd and write a
+ certificate request to the output.
+ */
+static int
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int inp_fd, out_fd;
+ FILE *out_fp;
+ int rc;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ if (inp_fd == -1)
+ return set_error (No_Input, NULL);
+ out_fd = assuan_get_output_fd (ctx);
+ if (out_fd == -1)
+ return set_error (No_Output, NULL);
+
+ out_fp = fdopen ( dup(out_fd), "w");
+ if (!out_fp)
+ return set_error (General_Error, "fdopen() failed");
+ rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
+ fclose (out_fp);
+
+ /* close and reset the fds */
+ assuan_close_input_fd (ctx);
+ assuan_close_output_fd (ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (ASSUAN_CONTEXT ctx)
+{
+ static struct {
+ const char *name;
+ int (*handler)(ASSUAN_CONTEXT, char *line);
+ } table[] = {
+ { "RECIPIENT", cmd_recipient },
+ { "SIGNER", cmd_signer },
+ { "ENCRYPT", cmd_encrypt },
+ { "DECRYPT", cmd_decrypt },
+ { "VERIFY", cmd_verify },
+ { "SIGN", cmd_sign },
+ { "IMPORT", cmd_import },
+ { "EXPORT", cmd_export },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "MESSAGE", cmd_message },
+ { "LISTKEYS", cmd_listkeys },
+ { "LISTSECRETKEYS",cmd_listsecretkeys },
+ { "GENKEY", cmd_genkey },
+ { "DELKEYS", cmd_delkeys },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+/* Startup the server */
+void
+gpgsm_server (void)
+{
+ int rc;
+ int filedes[2];
+ ASSUAN_CONTEXT ctx;
+ struct server_control_s ctrl;
+
+ memset (&ctrl, 0, sizeof ctrl);
+ gpgsm_init_default_ctrl (&ctrl);
+
+ /* For now we use a simple pipe based server so that we can work
+ from scripts. We will later add options to run as a daemon and
+ wait for requests on a Unix domain socket */
+ filedes[0] = 0;
+ filedes[1] = 1;
+ rc = assuan_init_pipe_server (&ctx, filedes);
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ assuan_strerror(rc));
+ gpgsm_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to the register commands with Assuan: %s\n",
+ assuan_strerror(rc));
+ gpgsm_exit (2);
+ }
+ assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready");
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_input_notify (ctx, input_notify);
+ assuan_register_output_notify (ctx, output_notify);
+ assuan_register_option_handler (ctx, option_handler);
+
+ assuan_set_pointer (ctx, &ctrl);
+ ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
+ ctrl.server_local->assuan_ctx = ctx;
+ ctrl.server_local->message_fd = -1;
+ ctrl.server_local->list_internal = 1;
+ ctrl.server_local->list_external = 0;
+
+ if (DBG_ASSUAN)
+ assuan_set_log_stream (ctx, log_get_stream ());
+
+ for (;;)
+ {
+ rc = assuan_accept (ctx);
+ if (rc == -1)
+ {
+ break;
+ }
+ else if (rc)
+ {
+ log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
+ continue;
+ }
+ }
+
+ gpgsm_release_certlist (ctrl.server_local->recplist);
+ ctrl.server_local->recplist = NULL;
+ gpgsm_release_certlist (ctrl.server_local->signerlist);
+ ctrl.server_local->signerlist = NULL;
+
+ assuan_deinit_server (ctx);
+}
+
+
+static const char *
+get_status_string ( int no )
+{
+ const char *s;
+
+ switch (no)
+ {
+ case STATUS_ENTER : s = "ENTER"; break;
+ case STATUS_LEAVE : s = "LEAVE"; break;
+ case STATUS_ABORT : s = "ABORT"; break;
+ case STATUS_GOODSIG: s = "GOODSIG"; break;
+ case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
+ case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
+ case STATUS_BADSIG : s = "BADSIG"; break;
+ case STATUS_ERRSIG : s = "ERRSIG"; break;
+ case STATUS_BADARMOR : s = "BADARMOR"; break;
+ case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
+ case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
+ case STATUS_TRUST_NEVER : s = "TRUST_NEVER"; break;
+ case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
+ case STATUS_TRUST_FULLY : s = "TRUST_FULLY"; break;
+ case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
+ case STATUS_GET_BOOL : s = "GET_BOOL"; break;
+ case STATUS_GET_LINE : s = "GET_LINE"; break;
+ case STATUS_GET_HIDDEN : s = "GET_HIDDEN"; break;
+ case STATUS_GOT_IT : s = "GOT_IT"; break;
+ case STATUS_SHM_INFO : s = "SHM_INFO"; break;
+ case STATUS_SHM_GET : s = "SHM_GET"; break;
+ case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL"; break;
+ case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
+ case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
+ case STATUS_VALIDSIG : s = "VALIDSIG"; break;
+ case STATUS_SIG_ID : s = "SIG_ID"; break;
+ case STATUS_ENC_TO : s = "ENC_TO"; break;
+ case STATUS_NODATA : s = "NODATA"; break;
+ case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
+ case STATUS_NO_PUBKEY : s = "NO_PUBKEY"; break;
+ case STATUS_NO_SECKEY : s = "NO_SECKEY"; break;
+ case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
+ case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
+ case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
+ case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
+ case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
+ case STATUS_GOODMDC : s = "GOODMDC"; break;
+ case STATUS_BADMDC : s = "BADMDC"; break;
+ case STATUS_ERRMDC : s = "ERRMDC"; break;
+ case STATUS_IMPORTED : s = "IMPORTED"; break;
+ case STATUS_IMPORT_RES : s = "IMPORT_RES"; break;
+ case STATUS_FILE_START : s = "FILE_START"; break;
+ case STATUS_FILE_DONE : s = "FILE_DONE"; break;
+ case STATUS_FILE_ERROR : s = "FILE_ERROR"; break;
+ case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
+ case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
+ case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
+ case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
+ case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
+ case STATUS_PROGRESS : s = "PROGRESS"; break;
+ case STATUS_SIG_CREATED : s = "SIG_CREATED"; break;
+ case STATUS_SESSION_KEY : s = "SESSION_KEY"; break;
+ case STATUS_NOTATION_NAME : s = "NOTATION_NAME" ; break;
+ case STATUS_NOTATION_DATA : s = "NOTATION_DATA" ; break;
+ case STATUS_POLICY_URL : s = "POLICY_URL" ; break;
+ case STATUS_BEGIN_STREAM : s = "BEGIN_STREAM"; break;
+ case STATUS_END_STREAM : s = "END_STREAM"; break;
+ case STATUS_KEY_CREATED : s = "KEY_CREATED"; break;
+ case STATUS_UNEXPECTED : s = "UNEXPECTED"; break;
+ case STATUS_INV_RECP : s = "INV_RECP"; break;
+ case STATUS_NO_RECP : s = "NO_RECP"; break;
+ case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
+ case STATUS_EXPSIG : s = "EXPSIG"; break;
+ case STATUS_EXPKEYSIG : s = "EXPKEYSIG"; break;
+ case STATUS_TRUNCATED : s = "TRUNCATED"; break;
+ case STATUS_ERROR : s = "ERROR"; break;
+ case STATUS_IMPORT_PROBLEM : s = "IMPORT_PROBLEM"; break;
+ default: s = "?"; break;
+ }
+ return s;
+}
+
+
+void
+gpgsm_status2 (CTRL ctrl, int no, ...)
+{
+ va_list arg_ptr;
+ const char *text;
+
+ va_start (arg_ptr, no);
+
+ if (ctrl->no_server)
+ {
+ if (ctrl->status_fd == -1)
+ return; /* no status wanted */
+ 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 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;
+ assuan_write_status (ctx, get_status_string (no), buf);
+ }
+
+ va_end (arg_ptr);
+}
+
+void
+gpgsm_status (CTRL ctrl, int no, const char *text)
+{
+ gpgsm_status2 (ctrl, no, text, NULL);
+}
+
+void
+gpgsm_status_with_err_code (CTRL ctrl, int no, const char *text,
+ gpg_err_code_t ec)
+{
+ char buf[30];
+
+ sprintf (buf, "%u", (unsigned int)ec);
+ if (text)
+ gpgsm_status2 (ctrl, no, text, buf, NULL);
+ else
+ gpgsm_status2 (ctrl, no, buf, NULL);
+}
+
+#if 0
+/*
+ * Write a status line with a buffer using %XX escapes. If WRAP is >
+ * 0 wrap the line after this length. If STRING is not NULL it will
+ * be prepended to the buffer, no escaping is done for string.
+ * A wrap of -1 forces spaces not to be encoded as %20.
+ */
+void
+write_status_text_and_buffer ( int no, const char *string,
+ const char *buffer, size_t len, int wrap )
+{
+ const char *s, *text;
+ int esc, first;
+ int lower_limit = ' ';
+ size_t n, count, dowrap;
+
+ if( !statusfp )
+ return; /* not enabled */
+
+ if (wrap == -1) {
+ lower_limit--;
+ wrap = 0;
+ }
+
+ text = get_status_string (no);
+ count = dowrap = first = 1;
+ do {
+ if (dowrap) {
+ fprintf (statusfp, "[GNUPG:] %s ", text );
+ count = dowrap = 0;
+ if (first && string) {
+ fputs (string, statusfp);
+ count += strlen (string);
+ }
+ first = 0;
+ }
+ for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
+ if ( *s == '%' || *(const byte*)s <= lower_limit
+ || *(const byte*)s == 127 )
+ esc = 1;
+ if ( wrap && ++count > wrap ) {
+ dowrap=1;
+ break;
+ }
+ }
+ if (esc) {
+ s--; n++;
+ }
+ if (s != buffer)
+ fwrite (buffer, s-buffer, 1, statusfp );
+ if ( esc ) {
+ fprintf (statusfp, "%%%02X", *(const byte*)s );
+ s++; n--;
+ }
+ buffer = s;
+ len = n;
+ if ( dowrap && len )
+ putc ( '\n', statusfp );
+ } while ( len );
+
+ putc ('\n',statusfp);
+ fflush (statusfp);
+}
+#endif