From e6ce8e80d57dfebcb3608056b2de24379fce991c Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Tue, 21 Apr 2009 21:36:59 -0400 Subject: [PATCH] Added the SET and UNSET command which replace the OPTION command. SET ITERATIONS requires an open file. Setting a value to "NULL" won't work like it used to. Use UNSET instead. --- doc/COMMANDS | 32 +++++---- src/commands.c | 212 +++++++++++++++++++++++++++++++++++++-------------------- src/pinentry.h | 2 + src/pwmd.c | 3 +- 4 files changed, 159 insertions(+), 90 deletions(-) diff --git a/doc/COMMANDS b/doc/COMMANDS index a42a6af9..f79059bd 100644 --- a/doc/COMMANDS +++ b/doc/COMMANDS @@ -161,10 +161,16 @@ VERSION Returns the server version number with a data response. -OPTION = +SET = Sets an option NAME to VALUE. See OPTIONS below for available options. +UNSET + Resets option NAME to the value specified in the server configuration + file. Some options have no default and will be reset to NULL or 0 + depending on the type. + + BYE Closes the connection disconnecting the client. Unless the SAVE command had been sent, any changes to the document will be lost. @@ -173,9 +179,9 @@ BYE OPTIONS ------- Commands that require a key that is neither cached nor specified will use -pinentry(1) to retrieve the key. Pinentry options can be set with the OPTION -command followed by the option name and value. Below are the available -pwmd options: +pinentry(1) to retrieve the key. Pinentry options can be set with the SET +command followed by the option name and value. Below are the available pwmd +options: NAME VALUE Description ---------|----------|---------------------------------------------------- @@ -194,26 +200,20 @@ pwmd options: LC_CTYPE Same as the --lc-ctype option to pinentry(1). LC_MESSAGES Same as the --lc-messages option to pinentry(1). ITERATIONS The number of encryption iterations to do when the - SAVE command is sent. When set, the configured - value will be updated to this value for the opened - file, or the "global" default if no file has been - opened yet. When setting this option before opening - a file, this option is reset to the data files - setting after opening. The CONFIG status message is + SAVE command is sent. An opened file is needed when + setting this option. The CONFIG status message is sent after receiving this command. CLIENT See below. -To reset an option value string to its default, set the value to NULL. - When pinentry is used with the SAVE command the passphrase will be asked for confirmation. If the confirmation fails, the process is started over again until either the passphrases match or until Cancel is selected. The OPEN command will only ask for the passphrase once without retrying on failure. It is up to the client to retry the OPEN command. Empty keys are allowed. To -prevent pinentry asking for an (empty) passphrase, set OPTION PINENTRY=0. +prevent pinentry asking for an (empty) passphrase, use SET PINENTRY=0. There is also a CLIENT option that contains other sub-options. The format is -OPTION CLIENT NAME=VALUE, where the option NAME is one of: +SET CLIENT NAME=VALUE, where the option NAME is one of: NAME VALUE Description ---------|----------|---------------------------------------------------- @@ -221,6 +221,8 @@ OPTION CLIENT NAME=VALUE, where the option NAME is one of: specified textual representation. Useful for debugging log messages. +To reset an option value string to its default, use the UNSET command. + STATUS MESSAGES --------------- @@ -393,7 +395,7 @@ The client_thread() uses an event to wait for the client FD to become ready for reading. When ready, it uses the libassuan external IO loop functions to prevent blocking letting other threads do their work. -When a key is required for the OPEN or SAVE commands, and OPTION PINENTRY=1, +When a key is required for the OPEN or SAVE commands, and SET PINENTRY=1, pinentry(1) is used to retrieve the key. This is done by creating a pipe() with the client_thread(), then pth_fork()'s the pinentry process which will write the key or error to the pipe that client_thread() will read from. After diff --git a/src/commands.c b/src/commands.c index f9982dfc..fdfa6537 100644 --- a/src/commands.c +++ b/src/commands.c @@ -974,7 +974,7 @@ gpg_error_t do_xml_encrypt(struct client_s *client, write_file: if (filename) { - if (!client && !strcmp(filename, "-")) { + if (!client && !g_strcmp0(filename, "-")) { crypto->fh->fd = STDOUT_FILENO; goto do_write_file; } @@ -1025,7 +1025,7 @@ do_write_file: if (len != sizeof(crypto->fh->fh2)) { len = errno; - if (filename && strcmp(filename, "-")) + if (filename && g_strcmp0(filename, "-")) unlink(tmp); return gpg_error_from_errno(len); @@ -1038,7 +1038,7 @@ do_write_file: pth_cancel_point(); len = errno; - if (filename && strcmp(filename, "-")) { + if (filename && g_strcmp0(filename, "-")) { unlink(tmp); pth_cancel_point(); } @@ -1050,13 +1050,13 @@ do_write_file: pth_cancel_point(); len = errno; - if (filename && strcmp(filename, "-")) + if (filename && g_strcmp0(filename, "-")) unlink(tmp); return gpg_error_from_errno(len); } - if (filename && strcmp(filename, "-")) { + if (filename && g_strcmp0(filename, "-")) { if (mode && get_key_file_boolean(filename, "backup") == TRUE) { gchar tmp2[FILENAME_MAX]; @@ -2520,7 +2520,7 @@ static int getconfig_command(assuan_context_t ctx, gchar *line) return send_syserror(ctx, ENOMEM); } - if (fp && !strcmp(paramp, "iterations")) { + if (fp && !g_strcmp0(paramp, "iterations")) { if (!(client->opts & OPT_ITERATIONS) || fp != client->filename) { file_header_internal_t *fh = read_file_header(fp, FALSE, &rc); @@ -2845,38 +2845,18 @@ static int version_command(assuan_context_t ctx, gchar *line) return send_error(ctx, rc); } -static void bye_notify(assuan_context_t ctx) -{ - struct client_s *cl = assuan_get_pointer(ctx); - - /* This will let assuan_process_next() return. */ - fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK); -} - -static void reset_notify(assuan_context_t ctx) -{ - struct client_s *cl = assuan_get_pointer(ctx); - - if (cl) - cleanup_client(cl); -} - -static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line) +static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *name, + const gchar *value) { - gchar name[32] = {0}, value[256] = {0}; struct client_s *cl = assuan_get_pointer(ctx); - if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2) - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX); - - if (g_strcasecmp(name, (gchar *)"NAME") == 0) { + if (g_ascii_strcasecmp(name, (gchar *)"NAME") == 0) { pth_attr_t attr = pth_attr_of(pth_self()); gchar buf[41]; - if (!strcmp(value, "NULL")) { - pth_attr_set(attr, PTH_ATTR_NAME, NULL); + if (!value) { pth_attr_destroy(attr); - return 0; + goto done; } print_fmt(buf, sizeof(buf), "%s", value); @@ -2887,101 +2867,188 @@ static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line) g_free(cl->pinentry->name); cl->pinentry->name = g_strdup(buf); + + if (!cl->pinentry->name) + return send_error(ctx, gpg_error_from_errno(ENOMEM)); #endif - log_write("OPTION CLIENT %s=%s", name, buf); } else - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION); + return send_error(ctx, + gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION)); - return 0; +done: + log_write("%s CLIENT %s%s%s", value ? "SET" : "UNSET", name, + value ? "=" : "", value ? value : ""); + return send_error(ctx, 0); } -static void set_option_value(gchar **opt, const gchar *val) +static void set_option_value(gchar **opt, const gchar *value) { if (opt) g_free(*opt); *opt = NULL; - if (val && strcmp(val, "NULL")) - *opt = g_strdup(val); + if (value) + *opt = g_strdup(value); } -static int option_handler(assuan_context_t ctx, const gchar *name, +static int set_unset_common(assuan_context_t ctx, const gchar *name, const gchar *value) { struct client_s *client = assuan_get_pointer(ctx); - if (!value || !*value) - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE); - - if (g_strcasecmp(name, (gchar *)"client") == 0) - return parse_client_option(ctx, value); - - if (g_strcasecmp(name, (gchar *)"iterations") == 0) { + if (g_ascii_strcasecmp(name, (gchar *)"iterations") == 0) { long n; gchar *p = NULL; + if (!client->filename) + return send_error(ctx, EPWMD_NO_FILE); + + if (!value) { + g_key_file_set_integer(keyfileh, client->filename, "iterations", + get_key_file_integer("global", "iterations")); + goto done; + } + errno = 0; n = strtol(value, &p, 10); if (errno || (p && *p) || n < 0) - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE); + return send_error(ctx, + gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE)); MUTEX_LOCK(&rcfile_mutex); - g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", (guint)n); + g_key_file_set_integer(keyfileh, + client->filename ? client->filename : "global", "iterations", + (guint)n); MUTEX_UNLOCK(&rcfile_mutex); - send_status_all(STATUS_CONFIG); if (client->filename) client->opts |= OPT_ITERATIONS; } #ifdef WITH_PINENTRY - else if (g_strcasecmp(name, (gchar *)"lc_messages") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"lc_messages") == 0) set_option_value(&client->pinentry->lcmessages, value); - else if (g_strcasecmp(name, (gchar *)"lc_ctype") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"lc_ctype") == 0) set_option_value(&client->pinentry->lcctype, value); - else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0) set_option_value(&client->pinentry->ttyname, value); - else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0) set_option_value(&client->pinentry->ttytype, value); - else if (g_strcasecmp(name, (gchar *)"display") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0) set_option_value(&client->pinentry->display, value); - else if (g_strcasecmp(name, (gchar *)"path") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"path") == 0) set_option_value(&client->pinentry->path, value); - else if (g_strcasecmp(name, (gchar *)"title") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"title") == 0) set_option_value(&client->pinentry->title, value); - else if (g_strcasecmp(name, (gchar *)"prompt") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"prompt") == 0) set_option_value(&client->pinentry->prompt, value); - else if (g_strcasecmp(name, (gchar *)"desc") == 0) + else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0) set_option_value(&client->pinentry->desc, value); - /* - * Look at client_thread() to see how this works. - */ - else if (g_strcasecmp(name, (gchar *)"timeout") == 0) { + else if (g_ascii_strcasecmp(name, (gchar *)"timeout") == 0) { gchar *p = NULL; - gint n = strtol(value, &p, 10); + gint n; + + if (!value) { + client->pinentry->timeout = + get_key_file_integer("global", "pinentry_timeout"); + goto done; + } + + n = strtol(value, &p, 10); if (*p || n < 0) - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE); + return send_error(ctx, + gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE)); client->pinentry->timeout = n; } - else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) { + else if (g_ascii_strcasecmp(name, (gchar *)"pinentry") == 0) { gchar *p = NULL; - gint n = strtol(value, &p, 10); + gint n; + + if (!value) { + client->pinentry->enable = get_key_file_boolean("global", + "enable_pinentry"); + goto done; + } + + n = strtol(value, &p, 10); if (*p || n < 0 || n > 1) - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE); + return send_error(ctx, + gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE)); client->pinentry->enable = n == 0 ? FALSE : TRUE; } #endif else - return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION); + return send_error(ctx, + gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION)); - log_write("OPTION %s=%s", name, value); - return 0; +done: + log_write("%s %s%s%s", value ? "SET" : "UNSET", name, + value ? "=" : "", value ? value : ""); + return send_error(ctx, 0); +} + +static int unset_command(assuan_context_t ctx, gchar *line) +{ + gchar *p = line; + + while (g_ascii_isspace(*p)) + p++; + + if (!g_ascii_strncasecmp(p, "CLIENT", 6)) { + p += 6; + + while (g_ascii_isspace(*p)) + p++; + + return parse_client_option(ctx, p, NULL); + } + + return set_unset_common(ctx, p, NULL); +} + +static int set_command(assuan_context_t ctx, gchar *line) +{ + gchar name[64] = {0}, value[256] = {0}; + gchar *p = line; + gboolean c = 0; + + while (g_ascii_isspace(*p)) + p++; + + if (!g_ascii_strncasecmp(p, "CLIENT", 6)) { + p += 6; + c = TRUE; + } + + if (sscanf(p, " %63[a-zA-Z] = %255c", name, value) != 2) + return send_error(ctx, gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX)); + + if (c) + return parse_client_option(ctx, name, value); + + return set_unset_common(ctx, name, value); +} + +static void bye_notify(assuan_context_t ctx) +{ + struct client_s *cl = assuan_get_pointer(ctx); + + /* This will let assuan_process_next() return. */ + fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK); +} + +static void reset_notify(assuan_context_t ctx) +{ + struct client_s *cl = assuan_get_pointer(ctx); + + if (cl) + cleanup_client(cl); } gpg_error_t register_commands(assuan_context_t ctx) @@ -3009,6 +3076,8 @@ gpg_error_t register_commands(assuan_context_t ctx) { "UNLOCK", unlock_command }, { "GETPID", getpid_command }, { "VERSION", version_command }, + { "SET", set_command }, + { "UNSET", unset_command }, { "INPUT", NULL }, { "OUTPUT", NULL }, { NULL, NULL } @@ -3027,11 +3096,6 @@ gpg_error_t register_commands(assuan_context_t ctx) if (rc) return rc; - rc = assuan_register_option_handler(ctx, option_handler); - - if (rc) - return rc; - rc = assuan_register_reset_notify(ctx, reset_notify); if (rc) diff --git a/src/pinentry.h b/src/pinentry.h index 387bd7a2..70516c46 100644 --- a/src/pinentry.h +++ b/src/pinentry.h @@ -21,6 +21,8 @@ #include +#define DEFAULT_PIN_TIMEOUT 30 + pth_mutex_t pin_mutex; gpg_error_t pinentry_fork(assuan_context_t ctx); diff --git a/src/pwmd.c b/src/pwmd.c index 56af628f..20f8bc54 100644 --- a/src/pwmd.c +++ b/src/pwmd.c @@ -727,7 +727,8 @@ static void set_rcfile_defaults(GKeyFile *kf) g_key_file_set_boolean(kf, "global", "enable_pinentry", TRUE); if (g_key_file_has_key(kf, "global", "pinentry_timeout", NULL) == FALSE) - g_key_file_set_integer(kf, "global", "pinentry_timeout", 20); + g_key_file_set_integer(kf, "global", "pinentry_timeout", + DEFAULT_PIN_TIMEOUT); #endif if (g_key_file_has_key(kf, "global", "xfer_progress", NULL) == FALSE) -- 2.11.4.GIT