diff options
author | Werner Koch <wk@gnupg.org> | 2009-04-01 12:51:53 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2009-04-01 12:51:53 +0200 |
commit | f8b4cd76501824d56d3cf78a8ba85291a62f0e6d (patch) | |
tree | cec902ba7d3dd1a38846805cf491a65b95bb79cd /common | |
parent | Implement decryption for TCOS 3 cards. (diff) | |
download | gnupg2-f8b4cd76501824d56d3cf78a8ba85291a62f0e6d.tar.xz gnupg2-f8b4cd76501824d56d3cf78a8ba85291a62f0e6d.zip |
Import/export of pkcs#12 now uses the gpg-agent directly.
Removed duplicated code (percent unescaping).
Diffstat (limited to 'common')
-rw-r--r-- | common/ChangeLog | 19 | ||||
-rw-r--r-- | common/Makefile.am | 5 | ||||
-rw-r--r-- | common/exechelp.c | 11 | ||||
-rw-r--r-- | common/get-passphrase.c | 277 | ||||
-rw-r--r-- | common/get-passphrase.h | 47 | ||||
-rw-r--r-- | common/membuf.c | 14 | ||||
-rw-r--r-- | common/percent.c | 155 | ||||
-rw-r--r-- | common/sysutils.c | 8 | ||||
-rw-r--r-- | common/t-percent.c | 20 | ||||
-rw-r--r-- | common/util.h | 5 |
10 files changed, 550 insertions, 11 deletions
diff --git a/common/ChangeLog b/common/ChangeLog index 1cf4f50fc..af804f490 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,22 @@ +2009-04-01 Werner Koch <wk@g10code.com> + + * exechelp.c (gnupg_spawn_process): Implement new flag bit 6. + * sysutils.c (gnupg_allow_set_foregound_window): Allow the use of + ASFW_ANY. + + * membuf.c (put_membuf, get_membuf): Wipe memory on out of core. + +2009-03-31 Werner Koch <wk@g10code.com> + + * percent.c (percent_unescape, percent_plus_unescape): New. + (percent_plus_unescape_inplace, percent_unescape_inplace): New. + (do_plus_or_plain_unescape, count_unescape, do_unescape): New. + (do_unescape_inplace): New. + * t-percent.c (test_percent_plus_escape): Test percent_plus_unescape. + + * get-passphrase.c, get-passphrase.h: New. + * Makefile.am (without_pth_sources): New. + 2009-03-18 Werner Koch <wk@g10code.com> * exechelp.c: Include sys/resource.h and sys/stat.h. diff --git a/common/Makefile.am b/common/Makefile.am index 72c7d179f..2ef324e18 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -71,9 +71,12 @@ common_sources = \ localename.c \ helpfile.c +# Sources only useful without PTH. +without_pth_sources = \ + get-passphrase.c get-passphrase.h -libcommon_a_SOURCES = $(common_sources) +libcommon_a_SOURCES = $(common_sources) $(without_pth_sources) if USE_DNS_SRV libcommon_a_SOURCES += srv.c endif diff --git a/common/exechelp.c b/common/exechelp.c index 3d1136609..7019ae7a0 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -52,6 +52,7 @@ #include "util.h" #include "i18n.h" +#include "sysutils.h" #include "exechelp.h" /* Define to 1 do enable debugging. */ @@ -471,6 +472,10 @@ gnupg_create_inbound_pipe (int filedes[2]) This flag is only useful under W32 systems, so that no new console is created and pops up a console window when starting the server + + Bit 6: On W32 run AllowSetForegroundWindow for the child. Due to + error problems this actually allows SetForegroundWindow for + childs of this process. Returns 0 on success or an error code. */ gpg_error_t @@ -568,6 +573,12 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + /* Fixme: For unknown reasons AllowSetForegroundWindow returns an + invalid argument error if we pass the the correct processID to + it. As a workaround we use -1 (ASFW_ANY). */ + if ( (flags & 64) ) + gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); /* Process has been created suspended; resume it now. */ ResumeThread (pi.hThread); diff --git a/common/get-passphrase.c b/common/get-passphrase.c new file mode 100644 index 000000000..68d7b706a --- /dev/null +++ b/common/get-passphrase.c @@ -0,0 +1,277 @@ +/* get-passphrase.c - Ask for a passphrase via the agent + * 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 <assert.h> +#include <assuan.h> + +#include "util.h" +#include "i18n.h" +#include "asshelp.h" +#include "membuf.h" +#include "sysutils.h" +#include "get-passphrase.h" + +/* The context used by this process to ask for the passphrase. */ +static assuan_context_t agent_ctx; +static struct +{ + gpg_err_source_t errsource; + int verbosity; + const char *homedir; + const char *agent_program; + const char *display; + const char *ttyname; + const char *ttytype; + const char *lc_ctype; + const char *lc_messages; + const char *xauthority; + const char *pinentry_user_data; +} agentargs; + + +/* Set local variable to be used for a possible agent startup. Note + that the strings are just pointers and should not anymore be + modified by the caller. */ +void +gnupg_prepare_get_passphrase (gpg_err_source_t errsource, + int verbosity, + const char *homedir, + const char *agent_program, + const char *opt_display, + const char *opt_ttyname, + const char *opt_ttytype, + const char *opt_lc_ctype, + const char *opt_lc_messages, + const char *opt_xauthority, + const char *opt_pinentry_user_data) +{ + agentargs.errsource = errsource; + agentargs.verbosity = verbosity; + agentargs.homedir = homedir; + agentargs.agent_program = agent_program; + agentargs.display = opt_display; + agentargs.ttyname = opt_ttyname; + agentargs.ttytype = opt_ttytype; + agentargs.lc_ctype = opt_lc_ctype; + agentargs.lc_messages = opt_lc_messages; + agentargs.xauthority = opt_xauthority; + agentargs.pinentry_user_data = opt_pinentry_user_data; +} + + +/* Try to connect to the agent via socket or fork it off and work by + pipes. Handle the server's initial greeting. */ +static gpg_error_t +start_agent (void) +{ + gpg_error_t err; + + /* Fixme: This code is not thread safe, thus we don't build it with + pth. We will need a context for each thread or serialize the + access to the agent. */ + if (agent_ctx) + return 0; + + err = start_new_gpg_agent (&agent_ctx, + agentargs.errsource, + agentargs.homedir, + agentargs.agent_program, + agentargs.display, + agentargs.ttyname, + agentargs.ttytype, + agentargs.lc_ctype, + agentargs.lc_messages, + agentargs.xauthority, + agentargs.pinentry_user_data, + agentargs.verbosity, 0, NULL, NULL); + if (!err) + { + /* Tell the agent that we support Pinentry notifications. No + error checking so that it will work with older agents. */ + assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", + NULL, NULL, NULL, NULL, NULL, NULL); + } + + return err; +} + + +/* This is the default inquiry callback. It merely handles the + Pinentry notification. */ +static int +default_inq_cb (void *opaque, const char *line) +{ + (void)opaque; + + if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) + { + gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); + /* We do not return errors to avoid breaking other code. */ + } + else + log_debug ("ignoring gpg-agent inquiry `%s'\n", line); + + return 0; +} + + +static int +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + membuf_t *data = opaque; + + if (buffer) + put_membuf (data, buffer, length); + return 0; +} + + +/* Ask for a passphrase via gpg-agent. On success the caller needs to + free the string stored at R_PASSPHRASE. On error NULL will be + stored at R_PASSPHRASE and an appropriate gpg error code is + returned. With REPEAT set to 1, gpg-agent will ask the user to + repeat the just entered passphrase. CACHE_ID is a gpg-agent style + passphrase cache id or NULL. ERR_MSG is a error message to be + presented to the user (e.g. "bad passphrase - try again") or NULL. + PROMPT is the prompt string to label the entry box, it may be NULL + for a default one. DESC_MSG is a longer description to be + displayed above the entry box, if may be NULL for a default one. + If USE_SECMEM is true, the returned passphrase is retruned in + secure memory. The length of all these strings is limited; they + need to fit in their encoded form into a standard Assuan line (i.e + less then about 950 characters). All strings shall be UTF-8. */ +gpg_error_t +gnupg_get_passphrase (const char *cache_id, + const char *err_msg, + const char *prompt, + const char *desc_msg, + int repeat, + int check_quality, + int use_secmem, + char **r_passphrase) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + const char *arg1 = NULL; + char *arg2 = NULL; + char *arg3 = NULL; + char *arg4 = NULL; + membuf_t data; + + *r_passphrase = NULL; + + err = start_agent (); + if (err) + return err; + + /* Check that the gpg-agent understands the repeat option. */ + if (assuan_transact (agent_ctx, + "GETINFO cmd_has_option GET_PASSPHRASE repeat", + NULL, NULL, NULL, NULL, NULL, NULL)) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + arg1 = cache_id && *cache_id? cache_id:NULL; + if (err_msg && *err_msg) + if (!(arg2 = percent_plus_escape (err_msg))) + goto no_mem; + if (prompt && *prompt) + if (!(arg3 = percent_plus_escape (prompt))) + goto no_mem; + if (desc_msg && *desc_msg) + if (!(arg4 = percent_plus_escape (desc_msg))) + goto no_mem; + + snprintf (line, DIM(line)-1, + "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s", + check_quality? "--check ":"", + repeat, + arg1? arg1:"X", + arg2? arg2:"X", + arg3? arg3:"X", + arg4? arg4:"X"); + line[DIM(line)-1] = 0; + xfree (arg2); + xfree (arg3); + xfree (arg4); + + if (use_secmem) + init_membuf_secure (&data, 64); + else + init_membuf (&data, 64); + err = assuan_transact (agent_ctx, line, + membuf_data_cb, &data, + default_inq_cb, NULL, NULL, NULL); + + /* Older Pinentries return the old assuan error code for canceled + which gets translated bt libassuan to GPG_ERR_ASS_CANCELED and + not to the code for a user cancel. Fix this here. */ + if (err && gpg_err_source (err) + && gpg_err_code (err) == GPG_ERR_ASS_CANCELED) + err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); + + if (err) + { + void *p; + size_t n; + + p = get_membuf (&data, &n); + if (p) + wipememory (p, n); + xfree (p); + } + else + { + put_membuf (&data, "", 1); + *r_passphrase = get_membuf (&data, NULL); + if (!*r_passphrase) + err = gpg_error_from_syserror (); + } + return err; + no_mem: + err = gpg_error_from_syserror (); + xfree (arg2); + xfree (arg3); + xfree (arg4); + return err; +} + + +/* Flush the passphrase cache with Id CACHE_ID. */ +gpg_error_t +gnupg_clear_passphrase (const char *cache_id) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + + if (!cache_id || !*cache_id) + return 0; + + err = start_agent (); + if (err) + return err; + + snprintf (line, DIM(line)-1, "CLEAR_PASSPHRASE %s", cache_id); + line[DIM(line)-1] = 0; + return assuan_transact (agent_ctx, line, NULL, NULL, + default_inq_cb, NULL, NULL, NULL); +} diff --git a/common/get-passphrase.h b/common/get-passphrase.h new file mode 100644 index 000000000..9457cdd3a --- /dev/null +++ b/common/get-passphrase.h @@ -0,0 +1,47 @@ +/* get-passphrase.h - Definitions to ask for a passphrase via the agent. + * 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/>. + */ + +#ifndef GNUPG_COMMON_GET_PASSPHRASE_H +#define GNUPG_COMMON_GET_PASSPHRASE_H + +void gnupg_prepare_get_passphrase (gpg_err_source_t errsource, + int verbosity, + const char *homedir, + const char *agent_program, + const char *opt_display, + const char *opt_ttyname, + const char *opt_ttytype, + const char *opt_lc_ctype, + const char *opt_lc_messages, + const char *opt_xauthority, + const char *opt_pinentry_user_data); + +gpg_error_t gnupg_get_passphrase (const char *cache_id, + const char *err_msg, + const char *prompt, + const char *desc_msg, + int repeat, + int check_quality, + int use_secmem, + char **r_passphrase); + +gpg_error_t gnupg_clear_passphrase (const char *cache_id); + + +#endif /*GNUPG_COMMON_GET_PASSPHRASE_H*/ diff --git a/common/membuf.c b/common/membuf.c index 9395eab6d..737930b76 100644 --- a/common/membuf.c +++ b/common/membuf.c @@ -1,5 +1,5 @@ -/* membuf.c - A simple implementation of a dynamic buffer - * Copyright (C) 2001, 2003 Free Software Foundation, Inc. +/* membuf.c - A simple implementation of a dynamic buffer. + * Copyright (C) 2001, 2003, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -75,7 +75,7 @@ put_membuf (membuf_t *mb, const void *buf, size_t len) in case we are storing sensitive data here. The membuf API does not provide another way to cleanup after an error. */ - memset (mb->buf, 0, mb->len); + wipememory (mb->buf, mb->len); return; } mb->buf = p; @@ -99,8 +99,12 @@ get_membuf (membuf_t *mb, size_t *len) if (mb->out_of_core) { - xfree (mb->buf); - mb->buf = NULL; + if (mb->buf) + { + wipememory (mb->buf, mb->len); + xfree (mb->buf); + mb->buf = NULL; + } errno = mb->out_of_core; return NULL; } diff --git a/common/percent.c b/common/percent.c index a0c78ec7b..e0c359289 100644 --- a/common/percent.c +++ b/common/percent.c @@ -1,5 +1,5 @@ /* percent.c - Percent escaping - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -21,6 +21,7 @@ #include <stdlib.h> #include <errno.h> #include <ctype.h> +#include <assert.h> #include "util.h" @@ -74,3 +75,155 @@ percent_plus_escape (const char *string) return buffer; } + + +/* Do the percent and plus/space unescaping from STRING to BUFFER and + return the length of the valid buffer. Plus unescaping is only + done if WITHPLUS is true. An escaped Nul character will be + replaced by NULREPL. */ +static size_t +do_unescape (unsigned char *buffer, const unsigned char *string, + int withplus, int nulrepl) +{ + unsigned char *p = buffer; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p = xtoi_2 (string); + if (!*p) + *p = nulrepl; + string++; + } + else if (*string == '+' && withplus) + *p = ' '; + else + *p = *string; + p++; + string++; + } + + return (p - buffer); +} + + +/* Count space required after unescaping STRING. Note that this will + never be larger than strlen (STRING). */ +static size_t +count_unescape (const unsigned char *string) +{ + size_t n = 0; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + string++; + } + string++; + n++; + } + + return n; +} + + +/* Helper. */ +static char * +do_plus_or_plain_unescape (const char *string, int withplus, int nulrepl) +{ + size_t nbytes, n; + char *newstring; + + nbytes = count_unescape (string); + newstring = xtrymalloc (nbytes+1); + if (newstring) + { + n = do_unescape (newstring, string, withplus, nulrepl); + assert (n == nbytes); + newstring[n] = 0; + } + return newstring; +} + + +/* Create a new allocated string from STRING with all "%xx" sequences + decoded and all plus signs replaced by a space. Embedded Nul + characters are replaced by the value of NULREPL. The function + returns the new string or NULL in case of a malloc failure. */ +char * +percent_plus_unescape (const char *string, int nulrepl) +{ + return do_plus_or_plain_unescape (string, 1, nulrepl); +} + + +/* Create a new allocated string from STRING with all "%xx" sequences + decoded. Embedded Nul characters are replaced by the value of + NULREPL. The function returns the new string or NULL in case of a + malloc failure. */ +char * +percent_unescape (const char *string, int nulrepl) +{ + return do_plus_or_plain_unescape (string, 0, nulrepl); +} + + +static size_t +do_unescape_inplace (char *string, int withplus, int nulrepl) +{ + unsigned char *p, *p0; + + p = p0 = string; + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p = xtoi_2 (string); + if (!*p) + *p = nulrepl; + string++; + } + else if (*string == '+' && withplus) + *p = ' '; + else + *p = *string; + p++; + string++; + } + + return (p - p0); +} + + +/* Perform percent and plus unescaping in STRING and return the new + valid length of the string. Embedded Nul characters are replaced + by the value of NULREPL. A terminating Nul character is not + inserted; the caller might want to call this function this way: + + foo[percent_plus_unescape_inplace (foo, 0)] = 0; + */ +size_t +percent_plus_unescape_inplace (char *string, int nulrepl) +{ + return do_unescape_inplace (string, 1, nulrepl); +} + + +/* Perform percent unescaping in STRING and return the new valid + length of the string. Embedded Nul characters are replaced by the + value of NULREPL. A terminating Nul character is not inserted; the + caller might want to call this function this way: + + foo[percent_unescape_inplace (foo, 0)] = 0; + */ +size_t +percent_unescape_inplace (char *string, int nulrepl) +{ + return do_unescape_inplace (string, 0, nulrepl); +} + diff --git a/common/sysutils.c b/common/sysutils.c index 0f1857ee3..8e0c75c1a 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -471,7 +471,9 @@ gnupg_reopen_std (const char *pgmname) if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) exit (3); -#endif /* HAVE_STAT && !HAVE_W32_SYSTEM */ +#else /* !(HAVE_STAT && !HAVE_W32_SYSTEM) */ + (void)pgmname; +#endif } @@ -479,11 +481,11 @@ gnupg_reopen_std (const char *pgmname) void gnupg_allow_set_foregound_window (pid_t pid) { - if (!pid || pid == (pid_t)(-1)) + if (!pid) log_info ("%s called with invalid pid %lu\n", "gnupg_allow_set_foregound_window", (unsigned long)pid); #ifdef HAVE_W32_SYSTEM - else if (!AllowSetForegroundWindow (pid)) + else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid)) log_info ("AllowSetForegroundWindow(%lu) failed: %s\n", (unsigned long)pid, w32_strerror (-1)); #endif diff --git a/common/t-percent.c b/common/t-percent.c index 93d95f78d..b8641b90b 100644 --- a/common/t-percent.c +++ b/common/t-percent.c @@ -66,8 +66,9 @@ test_percent_plus_escape (void) "%0A+ABC%09" }, { NULL, NULL } }; - char *buf; + char *buf, *buf2; int i; + size_t len; for (i=0; tbl[i].string; i++) { @@ -79,17 +80,34 @@ test_percent_plus_escape (void) } if (strcmp (buf, tbl[i].expect)) fail (i); + buf2 = percent_plus_unescape (buf, 0); + if (!buf2) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf2, tbl[i].string)) + fail (i); + xfree (buf2); + /* Now test the inplace conversion. */ + len = percent_plus_unescape_inplace (buf, 0); + buf[len] = 0; + if (strcmp (buf, tbl[i].string)) + fail (i); xfree (buf); } } + int main (int argc, char **argv) { (void)argc; (void)argv; + /* FIXME: We escape_unescape is not tested - only + percent_plus_unescape. */ test_percent_plus_escape (); return 0; diff --git a/common/util.h b/common/util.h index 3c664cec7..c64db9f6b 100644 --- a/common/util.h +++ b/common/util.h @@ -204,6 +204,11 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); +char *percent_plus_unescape (const char *string, int nulrepl); +char *percent_unescape (const char *string, int nulrepl); + +size_t percent_plus_unescape_inplace (char *string, int nulrepl); +size_t percent_unescape_inplace (char *string, int nulrepl); /*-- homedir.c --*/ |