[libgda] Handle authentification data in GdaQuarkList
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Handle authentification data in GdaQuarkList
- Date: Tue, 1 May 2012 18:22:28 +0000 (UTC)
commit d7bbac1acbb8b352e848f398e310704be3c77fc7
Author: Vivien Malerba <malerba gnome-db org>
Date: Tue May 1 19:58:19 2012 +0200
Handle authentification data in GdaQuarkList
by keeping it in a mangled version, and by avoiding cleartext
version to be swapped. (Clear text version is created when value
is requested)
configure.ac | 2 +
doc/C/libgda-sections.txt | 1 +
libgda/gda-quark-list.c | 310 ++++++++++++++++++++++++++++++++++++++++++---
libgda/gda-quark-list.h | 10 +-
libgda/libgda.symbols | 1 +
tests/.gitignore | 1 +
tests/Makefile.am | 8 +-
tests/test-quark-list.c | 96 ++++++++++++++
8 files changed, 404 insertions(+), 25 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 10522e0..c716b85 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,6 +64,8 @@ AC_CHECK_SIZEOF(unsigned int,0)
AC_CHECK_SIZEOF(unsigned long int,0)
AC_CHECK_TYPE(uint8_t, unsigned char)
AC_CHECK_FUNCS(localtime_r localtime_s)
+AC_CHECK_HEADER(sys/mman.h,
+ AC_CHECK_FUNC(mlock, [AC_DEFINE(USE_MLOCK, 1, [Use POSIX memory locking])]))
GDA_BUILDDATE=`date '+%F'`
AC_SUBST(GDA_BUILDDATE, $GDA_BUILDDATE)
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index 74351f1..8c7aed4 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -526,6 +526,7 @@ gda_quark_list_free
gda_quark_list_clear
gda_quark_list_add_from_string
gda_quark_list_find
+gda_quark_list_protect_values
gda_quark_list_remove
gda_quark_list_foreach
<SUBSECTION Standard>
diff --git a/libgda/gda-quark-list.c b/libgda/gda-quark-list.c
index 5b56e08..10b8abb 100644
--- a/libgda/gda-quark-list.c
+++ b/libgda/gda-quark-list.c
@@ -25,9 +25,29 @@
#include <libgda/gda-quark-list.h>
#include <libgda/gda-util.h>
+#include <string.h>
+
+#ifdef USE_MLOCK
+#include <sys/mman.h>
+#endif
+
+#define RANDOM_BLOB_SIZE 1024
+static gchar random_blob [RANDOM_BLOB_SIZE] = {0};
+static void ensure_static_blob_filled (void);
+
+typedef struct {
+ guint offset;/* offset in random_blob XOR from */
+ gchar *pvalue; /* XORed value, not 0 terminated */
+ gchar *cvalue; /* clear value, memory allocated with malloc() and mlock() */
+} ProtectedValue;
+
+static ProtectedValue *protected_value_new (gchar *cvalue);
+static void protected_value_free (ProtectedValue *pvalue);
+static void protected_value_xor (ProtectedValue *pvalue, gboolean to_clear);
struct _GdaQuarkList {
GHashTable *hash_table;
+ GHashTable *hash_protected;
};
GType gda_quark_list_get_type (void)
@@ -46,6 +66,25 @@ GType gda_quark_list_get_type (void)
*/
static void
+ensure_static_blob_filled (void)
+{
+ if (random_blob [0] == 0) {
+ guint i;
+ for (i = 0; i < RANDOM_BLOB_SIZE; i++) {
+ random_blob [i] = g_random_int_range (1, 255);
+ /*g_print ("%02x ", (guchar) random_blob [i]);*/
+ }
+#ifdef G_OS_WIN32
+ VirtualLock (random_blob, sizeof (gchar) * RANDOM_BLOB_SIZE);
+#else
+#ifdef USE_MLOCK
+ mlock (random_blob, sizeof (gchar) * RANDOM_BLOB_SIZE);
+#endif
+#endif
+ }
+}
+
+static void
copy_hash_pair (gpointer key, gpointer value, gpointer user_data)
{
g_hash_table_insert ((GHashTable *) user_data,
@@ -53,6 +92,117 @@ copy_hash_pair (gpointer key, gpointer value, gpointer user_data)
g_strdup ((const char *) value));
}
+guint
+protected_get_length (gchar *str, guint offset)
+{
+ gchar *ptr;
+ guint i;
+ ensure_static_blob_filled ();
+ for (i = 0, ptr = str; i < RANDOM_BLOB_SIZE - offset - 1; i++, ptr++) {
+ gchar c0, c1;
+ c0 = *ptr;
+ c1 = c0 ^ random_blob [offset + i];
+ if (!c1)
+ break;
+ }
+ return i;
+}
+
+static void
+protected_value_xor (ProtectedValue *pvalue, gboolean to_clear)
+{
+ if (to_clear) {
+ if (! pvalue->cvalue) {
+ guint i, l;
+ ensure_static_blob_filled ();
+ l = protected_get_length (pvalue->pvalue, pvalue->offset);
+ pvalue->cvalue = malloc (sizeof (gchar) * (l + 1));
+ for (i = 0; i < l; i++)
+ pvalue->cvalue [i] = pvalue->pvalue [i] ^ random_blob [pvalue->offset + i];
+ pvalue->cvalue [l] = 0;
+#ifdef G_OS_WIN32
+ VirtualLock (pvalue->cvalue, sizeof (gchar) * (l + 1));
+#else
+#ifdef USE_MLOCK
+ mlock (pvalue->cvalue, sizeof (gchar) * (l + 1));
+#endif
+#endif
+ /*g_print ("Unmangled [%s]\n", pvalue->cvalue);*/
+ }
+ }
+ else {
+ if (pvalue->cvalue) {
+ /*g_print ("Mangled [%s]\n", pvalue->cvalue);*/
+ guint i;
+ for (i = 0; ; i++) {
+ gchar c;
+ c = pvalue->cvalue[i];
+ pvalue->cvalue[i] = g_random_int_range (1, 255);
+ if (!c)
+ break;
+ }
+#ifdef G_OS_WIN32
+ VirtualUnLock (pvalue->cvalue, sizeof (gchar*) * (i + 1));
+#else
+#ifdef USE_MLOCK
+ munlock (pvalue->cvalue, sizeof (gchar*) * (i + 1));
+#endif
+#endif
+ free (pvalue->cvalue);
+ pvalue->cvalue = NULL;
+ }
+ }
+}
+
+static void
+copy_hash_pair_protected (gpointer key, gpointer value, gpointer user_data)
+{
+ ProtectedValue *p1, *p2;
+ guint l;
+ p1 = (ProtectedValue*) value;
+ p2 = g_new0 (ProtectedValue, 1);
+ l = protected_get_length (p1->pvalue, p1->offset);
+ p2->pvalue = g_new (gchar, l + 1);
+ memcpy (p2->pvalue, p1->pvalue, l + 1);
+ p2->offset = p1->offset;
+ p2->cvalue = NULL;
+ g_hash_table_insert ((GHashTable *) user_data,
+ g_strdup ((const char *) key), p2);
+}
+
+static ProtectedValue *
+protected_value_new (gchar *cvalue)
+{
+ ProtectedValue *pvalue;
+ guint i, l;
+ l = strlen (cvalue);
+ if (l >= RANDOM_BLOB_SIZE) {
+ g_warning ("Value too big to protect!");
+ return NULL;
+ }
+
+ /*g_print ("Initially mangled [%s]\n", cvalue);*/
+ ensure_static_blob_filled ();
+ pvalue = g_new (ProtectedValue, 1);
+ pvalue->offset = g_random_int_range (0, RANDOM_BLOB_SIZE - l - 2);
+ pvalue->pvalue = g_new (gchar, l + 1);
+ pvalue->cvalue = NULL;
+ for (i = 0; i <= l; i++) {
+ pvalue->pvalue [i] = cvalue [i] ^ random_blob [pvalue->offset + i];
+ cvalue [i] = g_random_int_range (0, 255);
+ }
+ return pvalue;
+}
+
+static void
+protected_value_free (ProtectedValue *pvalue)
+{
+ g_free (pvalue->pvalue);
+ if (pvalue->cvalue)
+ protected_value_xor (pvalue, FALSE);
+ g_free (pvalue);
+}
+
/**
* gda_quark_list_new:
*
@@ -71,7 +221,8 @@ gda_quark_list_new (void)
GdaQuarkList *qlist;
qlist = g_new0 (GdaQuarkList, 1);
- qlist->hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ qlist->hash_table = NULL;
+ qlist->hash_protected = NULL;
return qlist;
}
@@ -116,7 +267,10 @@ gda_quark_list_clear (GdaQuarkList *qlist)
{
g_return_if_fail (qlist != NULL);
- g_hash_table_remove_all (qlist->hash_table);
+ if (qlist->hash_table)
+ g_hash_table_remove_all (qlist->hash_table);
+ if (qlist->hash_protected)
+ g_hash_table_remove_all (qlist->hash_protected);
}
/**
@@ -130,7 +284,10 @@ gda_quark_list_free (GdaQuarkList *qlist)
{
g_return_if_fail (qlist != NULL);
- g_hash_table_destroy (qlist->hash_table);
+ if (qlist->hash_table)
+ g_hash_table_destroy (qlist->hash_table);
+ if (qlist->hash_protected)
+ g_hash_table_destroy (qlist->hash_protected);
g_free (qlist);
}
@@ -152,12 +309,33 @@ gda_quark_list_copy (GdaQuarkList *qlist)
g_return_val_if_fail (qlist != NULL, NULL);
new_qlist = gda_quark_list_new ();
- g_hash_table_foreach (qlist->hash_table,
- copy_hash_pair,
- new_qlist->hash_table);
+ if (qlist->hash_table) {
+ new_qlist->hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+ g_hash_table_foreach (qlist->hash_table, copy_hash_pair, new_qlist->hash_table);
+ }
+ if (qlist->hash_protected) {
+ new_qlist->hash_protected = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) protected_value_free);
+ g_hash_table_foreach (qlist->hash_protected, copy_hash_pair_protected,
+ new_qlist->hash_protected);
+ }
return new_qlist;
}
+static gboolean
+name_is_protected (const gchar *name)
+{
+ if (!g_ascii_strncasecmp (name, "pass", 4) ||
+ !g_ascii_strncasecmp (name, "username", 8))
+ return TRUE;
+ else
+ return FALSE;
+}
+
/**
* gda_quark_list_add_from_string
* @qlist: a #GdaQuarkList.
@@ -215,8 +393,37 @@ gda_quark_list_add_from_string (GdaQuarkList *qlist, const gchar *string, gboole
g_strstrip (value);
gda_rfc1738_decode (value);
}
- g_hash_table_insert (qlist->hash_table,
- (gpointer) name, (gpointer) value);
+
+ if (! name_is_protected (name)) {
+ if (!qlist->hash_table)
+ qlist->hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+ g_hash_table_insert (qlist->hash_table,
+ (gpointer) name, (gpointer) value);
+ }
+ else {
+ ProtectedValue *pvalue;
+ pvalue = protected_value_new (value);
+ if (pvalue) {
+ if (!qlist->hash_protected)
+ qlist->hash_protected = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) protected_value_free);
+ g_hash_table_insert (qlist->hash_protected,
+ (gpointer) name, (gpointer) pvalue);
+ g_free (value); /* has been mangled */
+ }
+ else {
+ if (!qlist->hash_table)
+ qlist->hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+ g_hash_table_insert (qlist->hash_table,
+ (gpointer) name, (gpointer) value);
+ }
+ }
g_free (pair);
}
else
@@ -232,21 +439,34 @@ gda_quark_list_add_from_string (GdaQuarkList *qlist, const gchar *string, gboole
* @qlist: a #GdaQuarkList.
* @name: the name of the value to search for.
*
- * Searches for the value identified by @name in the given #GdaQuarkList.
+ * Searches for the value identified by @name in the given #GdaQuarkList. For protected values
+ * (authentification data), don't forget to call gda_quark_list_protect_values() when you
+ * don't need them anymore (when needed again, they will be unmangled again).
*
- * Returns: the value associated with the given key if found, or %NULL
- * if not found.
+ * Returns: the value associated with the given key if found, or %NULL if not found.
*/
const gchar *
gda_quark_list_find (GdaQuarkList *qlist, const gchar *name)
{
- gchar *value;
-
- g_return_val_if_fail (qlist != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- value = g_hash_table_lookup (qlist->hash_table, name);
- return (const gchar *) value;
+ gchar *value = NULL;
+ g_return_val_if_fail (qlist, NULL);
+ g_return_val_if_fail (name, NULL);
+
+ if (qlist->hash_table)
+ value = g_hash_table_lookup (qlist->hash_table, name);
+ if (value)
+ return value;
+
+ ProtectedValue *pvalue = NULL;
+ if (qlist->hash_protected)
+ pvalue = g_hash_table_lookup (qlist->hash_protected, name);
+ if (pvalue) {
+ if (! pvalue->cvalue)
+ protected_value_xor (pvalue, TRUE);
+ return pvalue->cvalue;
+ }
+ else
+ return NULL;
}
/**
@@ -259,10 +479,31 @@ gda_quark_list_find (GdaQuarkList *qlist, const gchar *name)
void
gda_quark_list_remove (GdaQuarkList *qlist, const gchar *name)
{
+ gboolean removed = FALSE;
g_return_if_fail (qlist != NULL);
g_return_if_fail (name != NULL);
- g_hash_table_remove (qlist->hash_table, name);
+ if (qlist->hash_table && g_hash_table_remove (qlist->hash_table, name))
+ removed = TRUE;
+ if (!removed && qlist->hash_protected)
+ g_hash_table_remove (qlist->hash_protected, name);
+}
+
+typedef struct {
+ gpointer user_data;
+ GHFunc func;
+} PFuncData;
+
+static void
+p_foreach (gchar *key, ProtectedValue *pvalue, PFuncData *data)
+{
+ if (pvalue->cvalue)
+ data->func ((gpointer) key, (gpointer) pvalue->cvalue, data->user_data);
+ else {
+ protected_value_xor (pvalue, TRUE);
+ data->func ((gpointer) key, (gpointer) pvalue->cvalue, data->user_data);
+ protected_value_xor (pvalue, FALSE);
+ }
}
/**
@@ -277,7 +518,34 @@ gda_quark_list_remove (GdaQuarkList *qlist, const gchar *name)
void
gda_quark_list_foreach (GdaQuarkList *qlist, GHFunc func, gpointer user_data)
{
- g_return_if_fail (qlist != NULL);
+ g_return_if_fail (qlist);
- g_hash_table_foreach (qlist->hash_table, func, user_data);
+ if (qlist->hash_table)
+ g_hash_table_foreach (qlist->hash_table, func, user_data);
+ if (qlist->hash_protected)
+ g_hash_table_foreach (qlist->hash_protected, (GHFunc) p_foreach, user_data);
+}
+
+static void
+protect_foreach (G_GNUC_UNUSED gchar *key, ProtectedValue *pvalue, G_GNUC_UNUSED gpointer data)
+{
+ if (pvalue && pvalue->cvalue)
+ protected_value_xor (pvalue, FALSE);
+}
+
+/**
+ * gda_quark_list_protect_values:
+ * @qlist: a #GdaQuarkList
+ *
+ * Call this function to get rid of the clear version of the value associated to
+ * @name.
+ *
+ * Since: 5.2.0
+ */
+void
+gda_quark_list_protect_values (GdaQuarkList *qlist)
+{
+ g_return_if_fail (qlist);
+ if (qlist->hash_protected)
+ g_hash_table_foreach (qlist->hash_protected, (GHFunc) protect_foreach, NULL);
}
diff --git a/libgda/gda-quark-list.h b/libgda/gda-quark-list.h
index 9b34a7b..897a2a2 100644
--- a/libgda/gda-quark-list.h
+++ b/libgda/gda-quark-list.h
@@ -40,8 +40,12 @@ typedef struct _GdaQuarkList GdaQuarkList;
* @stability: Stable
* @see_also:
*
- * This object is used mainly by database providers' implementations to parse connection
- * strings into lists of KEY=VALUE pairs.
+ * This object is used to store KEY=VALUE pairs. It is mainly used internally by Libgda to store connection
+ * parameters.
+ *
+ * Authentification values are kept in a mangled form in memory, and unmangled when
+ * they are requested using gda_quark_list_find(), and when you don't need them anymore,
+ * call gda_quark_list_protect_values() to remove the unmangled version.
*/
GType gda_quark_list_get_type (void) G_GNUC_CONST;
@@ -54,6 +58,8 @@ void gda_quark_list_add_from_string (GdaQuarkList *qlist,
const gchar *string,
gboolean cleanup);
const gchar *gda_quark_list_find (GdaQuarkList *qlist, const gchar *name);
+void gda_quark_list_protect_values (GdaQuarkList *qlist);
+
void gda_quark_list_remove (GdaQuarkList *qlist, const gchar *name);
void gda_quark_list_clear (GdaQuarkList *qlist);
void gda_quark_list_foreach (GdaQuarkList *qlist, GHFunc func, gpointer user_data);
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index a10f481..6815eee 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -540,6 +540,7 @@
gda_quark_list_get_type
gda_quark_list_new
gda_quark_list_new_from_string
+ gda_quark_list_protect_values
gda_quark_list_remove
gda_repetitive_statement_append_set
gda_repetitive_statement_get_all_sets
diff --git a/tests/.gitignore b/tests/.gitignore
index a56365c..232b794 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -5,3 +5,4 @@ test-identifiers-quotes
test-sql-builder
test-connection-string-split
test-input-parsers
+test-quark-list
\ No newline at end of file
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f47077a..dec1cce 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,8 +1,8 @@
noinst_LTLIBRARIES = libgda-test-5.0.la
TESTS_ENVIRONMENT = GDA_TOP_SRC_DIR="$(abs_top_srcdir)" GDA_TOP_BUILD_DIR="$(abs_top_builddir)"
-TESTS = test-ddl-creator test-bin-converter test-sql-identifier test-identifiers-quotes test-sql-builder test-connection-string-split test-input-parsers
-check_PROGRAMS = test-ddl-creator test-bin-converter test-sql-identifier test-identifiers-quotes test-sql-builder test-connection-string-split test-input-parsers
+TESTS = test-ddl-creator test-bin-converter test-sql-identifier test-identifiers-quotes test-sql-builder test-connection-string-split test-input-parsers test-quark-list
+check_PROGRAMS = test-ddl-creator test-bin-converter test-sql-identifier test-identifiers-quotes test-sql-builder test-connection-string-split test-input-parsers test-quark-list
if ENABLE_VALA_EXTENSIONS
VALA_EXTENSIONS= vala
@@ -92,4 +92,8 @@ test_input_parsers_LDADD = \
$(top_builddir)/libgda/libgda-5.0.la \
$(COREDEPS_LIBS)
+test_quark_list_LDADD = \
+ $(top_builddir)/libgda/libgda-5.0.la \
+ $(COREDEPS_LIBS)
+
EXTRA_DIST = dbstruct.xml
diff --git a/tests/test-quark-list.c b/tests/test-quark-list.c
new file mode 100644
index 0000000..086ea8c
--- /dev/null
+++ b/tests/test-quark-list.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program 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.
+ *
+ * This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <libgda/libgda.h>
+#include <string.h>
+
+guint
+test_quark (GdaQuarkList *ql, guint *out_ntests)
+{
+ const gchar *tmp;
+ guint nfailed = 0;
+ guint ntests = 0;
+
+ ntests ++;
+ tmp = gda_quark_list_find (ql, "PARAM");
+ if (!tmp || strcmp (tmp, "value"))
+ nfailed++;
+
+ ntests ++;
+ tmp = gda_quark_list_find (ql, "PASSWORD");
+ if (!tmp || strcmp (tmp, "*mypass*"))
+ nfailed++;
+
+ ntests ++;
+ tmp = gda_quark_list_find (ql, "PASSWORD");
+ if (!tmp || strcmp (tmp, "*mypass*"))
+ nfailed++;
+
+ gda_quark_list_protect_values (ql);
+
+ ntests ++;
+ tmp = gda_quark_list_find (ql, "PASSWORD");
+ if (!tmp || strcmp (tmp, "*mypass*"))
+ nfailed++;
+
+ ntests ++;
+ tmp = gda_quark_list_find (ql, "USERNAME");
+ if (!tmp || strcmp (tmp, "dirch"))
+ nfailed++;
+
+ gda_quark_list_remove (ql, "PASSWORD");
+
+ ntests ++;
+ tmp = gda_quark_list_find (ql, "PASSWORD");
+ if (tmp)
+ nfailed++;
+
+ gda_quark_list_protect_values (ql);
+
+ if (out_ntests)
+ *out_ntests = ntests;
+ return nfailed;
+}
+
+int
+main (int argc, char** argv)
+{
+ GdaQuarkList *ql, *ql2;
+ guint nfailed = 0;
+ guint ntests = 0;
+ guint tmp;
+
+ ql = gda_quark_list_new_from_string ("PARAM=value;PASSWORD=*mypass*;USERNAME=dirch");
+ ql2 = gda_quark_list_copy (ql);
+ nfailed = test_quark (ql, &ntests);
+ nfailed += test_quark (ql2, &tmp);
+ ntests += tmp;
+
+ /* out */
+ gda_quark_list_free (ql);
+ gda_quark_list_free (ql2);
+
+ if (nfailed == 0) {
+ g_print ("Ok (%d tests passed)\n", ntests);
+ return EXIT_SUCCESS;
+ }
+ else {
+ g_print ("Failed (%d tests failed out of %d)\n", nfailed, ntests);
+ return EXIT_FAILURE;
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]