summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/ChangeLog19
-rw-r--r--common/Makefile.am5
-rw-r--r--common/exechelp.c11
-rw-r--r--common/get-passphrase.c277
-rw-r--r--common/get-passphrase.h47
-rw-r--r--common/membuf.c14
-rw-r--r--common/percent.c155
-rw-r--r--common/sysutils.c8
-rw-r--r--common/t-percent.c20
-rw-r--r--common/util.h5
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 --*/