1 /*
2  * Copyright (C) 2001 - 2003 Rodrigo Moya <rodrigo@gnome-db.org>
3  * Copyright (C) 2001 - 2012 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2002 Gonzalo Paniagua Javier <gonzalo@src.gnome.org>
5  * Copyright (C) 2003 Laurent Sansonetti <laurent@datarescue.be>
6  * Copyright (C) 2006 Murray Cumming <murrayc@murrayc.com>
7  * Copyright (C) 2008 Przemysław Grzegorczyk <pgrzegorczyk@gmail.com>
8  * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23  * Boston, MA  02110-1301, USA.
24  */
25 
26 #include <libgda/gda-quark-list.h>
27 #include <libgda/gda-util.h>
28 #include <string.h>
29 
30 #ifdef USE_MLOCK
31 #include <sys/mman.h>
32 #endif
33 #ifdef G_OS_WIN32
34 #include <windows.h>
35 #include <winbase.h>
36 #endif
37 
38 #define RANDOM_BLOB_SIZE 1024
39 static gchar random_blob [RANDOM_BLOB_SIZE] = {0};
40 static void ensure_static_blob_filled (void);
41 
42 typedef struct {
43 	guint  offset;/* offset in random_blob XOR from */
44 	gchar *pvalue; /* XORed value, not 0 terminated */
45 	gchar *cvalue; /* clear value, memory allocated with malloc() and mlock() */
46 } ProtectedValue;
47 
48 static ProtectedValue *protected_value_new (gchar *cvalue);
49 static void protected_value_free (ProtectedValue *pvalue);
50 static void protected_value_xor (ProtectedValue *pvalue, gboolean to_clear);
51 
52 struct _GdaQuarkList {
53 	GHashTable *hash_table;
54 	GHashTable *hash_protected;
55 };
56 
gda_quark_list_get_type(void)57 GType gda_quark_list_get_type (void)
58 {
59 	static GType our_type = 0;
60 
61 	if (our_type == 0)
62 		our_type = g_boxed_type_register_static ("GdaQuarkList",
63 			(GBoxedCopyFunc) gda_quark_list_copy,
64 			(GBoxedFreeFunc) gda_quark_list_free);
65 	return our_type;
66 }
67 
68 /*
69  * Private functions
70  */
71 
72 static void
ensure_static_blob_filled(void)73 ensure_static_blob_filled (void)
74 {
75 	if (random_blob [0] == 0) {
76 		guint i;
77 		for (i = 0; i < RANDOM_BLOB_SIZE; i++) {
78 			random_blob [i]	= g_random_int_range (1, 255);
79 			/*g_print ("%02x ", (guchar) random_blob [i]);*/
80 		}
81 #ifdef G_OS_WIN32
82 		VirtualLock (random_blob, sizeof (gchar) * RANDOM_BLOB_SIZE);
83 #else
84 #ifdef USE_MLOCK
85 		mlock (random_blob, sizeof (gchar) * RANDOM_BLOB_SIZE);
86 #endif
87 #endif
88 	}
89 }
90 
91 static void
copy_hash_pair(gpointer key,gpointer value,gpointer user_data)92 copy_hash_pair (gpointer key, gpointer value, gpointer user_data)
93 {
94 	g_hash_table_insert ((GHashTable *) user_data,
95 			     g_strdup ((const char *) key),
96 			     g_strdup ((const char *) value));
97 }
98 
99 guint
protected_get_length(gchar * str,guint offset)100 protected_get_length (gchar *str, guint offset)
101 {
102 	gchar *ptr;
103 	guint i;
104 	ensure_static_blob_filled ();
105 	for (i = 0, ptr = str; i < RANDOM_BLOB_SIZE - offset - 1; i++, ptr++) {
106 		gchar c0, c1;
107 		c0 = *ptr;
108 		c1 = c0 ^ random_blob [offset + i];
109 		if (!c1)
110 			break;
111 	}
112 	return i;
113 }
114 
115 static void
protected_value_xor(ProtectedValue * pvalue,gboolean to_clear)116 protected_value_xor (ProtectedValue *pvalue, gboolean to_clear)
117 {
118 	if (to_clear) {
119 		if (! pvalue->cvalue) {
120 			guint i, l;
121 			ensure_static_blob_filled ();
122 			l = protected_get_length (pvalue->pvalue, pvalue->offset);
123 			pvalue->cvalue = malloc (sizeof (gchar) * (l + 1));
124 			for (i = 0; i < l; i++)
125 				pvalue->cvalue [i] = pvalue->pvalue [i] ^ random_blob [pvalue->offset + i];
126 			pvalue->cvalue [l] = 0;
127 #ifdef G_OS_WIN32
128 			VirtualLock (pvalue->cvalue, sizeof (gchar) * (l + 1));
129 #else
130 #ifdef USE_MLOCK
131 			mlock (pvalue->cvalue, sizeof (gchar) * (l + 1));
132 #endif
133 #endif
134 			/*g_print ("Unmangled [%s]\n", pvalue->cvalue);*/
135 		}
136 	}
137 	else {
138 		if (pvalue->cvalue) {
139 			/*g_print ("Mangled [%s]\n", pvalue->cvalue);*/
140 			guint i;
141 			for (i = 0; ; i++) {
142 				gchar c;
143 				c = pvalue->cvalue[i];
144 				pvalue->cvalue[i] = g_random_int_range (1, 255);
145 				if (!c)
146 					break;
147 			}
148 #ifdef G_OS_WIN32
149 			VirtualUnlock (pvalue->cvalue, sizeof (gchar) * (i + 1));
150 #else
151 #ifdef USE_MLOCK
152 			munlock (pvalue->cvalue, sizeof (gchar) * (i + 1));
153 #endif
154 #endif
155 			free (pvalue->cvalue);
156 			pvalue->cvalue = NULL;
157 		}
158 	}
159 }
160 
161 static void
copy_hash_pair_protected(gpointer key,gpointer value,gpointer user_data)162 copy_hash_pair_protected (gpointer key, gpointer value, gpointer user_data)
163 {
164 	ProtectedValue *p1, *p2;
165 	guint l;
166 	p1 = (ProtectedValue*) value;
167 	p2 = g_new0 (ProtectedValue, 1);
168 	l = protected_get_length (p1->pvalue, p1->offset);
169 	p2->pvalue = g_new (gchar, l + 1);
170 	memcpy (p2->pvalue, p1->pvalue, l + 1);
171 	p2->offset = p1->offset;
172 	p2->cvalue = NULL;
173 	g_hash_table_insert ((GHashTable *) user_data,
174 			     g_strdup ((const char *) key), p2);
175 }
176 
177 static ProtectedValue *
protected_value_new(gchar * cvalue)178 protected_value_new (gchar *cvalue)
179 {
180 	ProtectedValue *pvalue;
181 	guint i, l;
182 	l = strlen (cvalue);
183 	if (l >= RANDOM_BLOB_SIZE) {
184 		g_warning ("Value too big to protect!");
185 		return NULL;
186 	}
187 
188 	/*g_print ("Initially mangled [%s]\n", cvalue);*/
189 	ensure_static_blob_filled ();
190 	pvalue = g_new (ProtectedValue, 1);
191 	pvalue->offset = g_random_int_range (0, RANDOM_BLOB_SIZE - l - 2);
192 	pvalue->pvalue = g_new (gchar, l + 1);
193 	pvalue->cvalue = NULL;
194 	for (i = 0; i <= l; i++) {
195 		pvalue->pvalue [i] = cvalue [i] ^ random_blob [pvalue->offset + i];
196 		cvalue [i] = g_random_int_range (0, 255);
197 	}
198 	return pvalue;
199 }
200 
201 static void
protected_value_free(ProtectedValue * pvalue)202 protected_value_free (ProtectedValue *pvalue)
203 {
204 	g_free (pvalue->pvalue);
205 	if (pvalue->cvalue)
206 		protected_value_xor (pvalue, FALSE);
207 	g_free (pvalue);
208 }
209 
210 /**
211  * gda_quark_list_new:
212  *
213  * Creates a new #GdaQuarkList, which is a set of key->value pairs,
214  * very similar to GLib's GHashTable, but with the only purpose to
215  * make easier the parsing and creation of data source connection
216  * strings.
217  *
218  * Returns: (transfer full): the newly created #GdaQuarkList.
219  *
220  * Free-function: gda_quark_list_free
221  */
222 GdaQuarkList *
gda_quark_list_new(void)223 gda_quark_list_new (void)
224 {
225 	GdaQuarkList *qlist;
226 
227 	qlist = g_new0 (GdaQuarkList, 1);
228 	qlist->hash_table = NULL;
229 	qlist->hash_protected = NULL;
230 
231 	return qlist;
232 }
233 
234 /**
235  * gda_quark_list_new_from_string:
236  * @string: a string.
237  *
238  * Creates a new #GdaQuarkList given a string.
239  *
240  * @string must be a semi-colon separated list of "&lt;key&gt;=&lt;value&gt;" strings (for example
241  * "DB_NAME=notes;USERNAME=alfred"). Each key and value must respect the RFC 1738 recommendations: the
242  * <constant>&lt;&gt;&quot;#%{}|\^~[]&apos;`;/?:@=&amp;</constant> and space characters are replaced by
243  * <constant>&quot;%%ab&quot;</constant> where
244  * <constant>ab</constant> is the hexadecimal number corresponding to the character (for example the
245  * "DB_NAME=notes;USERNAME=al%%20fred" string will specify a username as "al fred"). If this formalism
246  * is not respected, then some unexpected results may occur.
247  *
248  * Returns: (transfer full): the newly created #GdaQuarkList.
249  *
250  * Free-function: gda_quark_list_free
251  */
252 GdaQuarkList *
gda_quark_list_new_from_string(const gchar * string)253 gda_quark_list_new_from_string (const gchar *string)
254 {
255 	GdaQuarkList *qlist;
256 
257 	qlist = gda_quark_list_new ();
258 	gda_quark_list_add_from_string (qlist, string, FALSE);
259 
260 	return qlist;
261 }
262 
263 /**
264  * gda_quark_list_clear:
265  * @qlist: a #GdaQuarkList.
266  *
267  * Removes all strings in the given #GdaQuarkList.
268  */
269 void
gda_quark_list_clear(GdaQuarkList * qlist)270 gda_quark_list_clear (GdaQuarkList *qlist)
271 {
272 	g_return_if_fail (qlist != NULL);
273 
274 	if (qlist->hash_table)
275 		g_hash_table_remove_all (qlist->hash_table);
276 	if (qlist->hash_protected)
277 		g_hash_table_remove_all (qlist->hash_protected);
278 }
279 
280 /**
281  * gda_quark_list_free:
282  * @qlist: a #GdaQuarkList.
283  *
284  * Releases all memory occupied by the given #GdaQuarkList.
285  */
286 void
gda_quark_list_free(GdaQuarkList * qlist)287 gda_quark_list_free (GdaQuarkList *qlist)
288 {
289 	g_return_if_fail (qlist != NULL);
290 
291 	if (qlist->hash_table)
292 		g_hash_table_destroy (qlist->hash_table);
293 	if (qlist->hash_protected)
294 		g_hash_table_destroy (qlist->hash_protected);
295 
296 	g_free (qlist);
297 }
298 
299 
300 /**
301  * gda_quark_list_copy:
302  * @qlist: quark_list to get a copy from.
303  *
304  * Creates a new #GdaQuarkList from an existing one.
305  *
306  * Returns: a newly allocated #GdaQuarkList with a copy of the data in @qlist.
307  */
308 GdaQuarkList *
gda_quark_list_copy(GdaQuarkList * qlist)309 gda_quark_list_copy (GdaQuarkList *qlist)
310 {
311 	GdaQuarkList *new_qlist;
312 
313 	g_return_val_if_fail (qlist != NULL, NULL);
314 
315 	new_qlist = gda_quark_list_new ();
316 	if (qlist->hash_table) {
317 		new_qlist->hash_table = g_hash_table_new_full (g_str_hash,
318 							       g_str_equal,
319 							       g_free, g_free);
320 		g_hash_table_foreach (qlist->hash_table, copy_hash_pair, new_qlist->hash_table);
321 	}
322 	if (qlist->hash_protected) {
323 		new_qlist->hash_protected = g_hash_table_new_full (g_str_hash,
324 								   g_str_equal,
325 								   g_free,
326 								   (GDestroyNotify) protected_value_free);
327 		g_hash_table_foreach (qlist->hash_protected, copy_hash_pair_protected,
328 				      new_qlist->hash_protected);
329 	}
330 	return new_qlist;
331 }
332 
333 static gboolean
name_is_protected(const gchar * name)334 name_is_protected (const gchar *name)
335 {
336 	if (!g_ascii_strncasecmp (name, "pass", 4) ||
337 	    !g_ascii_strncasecmp (name, "username", 8))
338 		return TRUE;
339 	else
340 		return FALSE;
341 }
342 
343 /**
344  * gda_quark_list_add_from_string:
345  * @qlist: a #GdaQuarkList.
346  * @string: a string.
347  * @cleanup: whether to cleanup the previous content or not.
348  *
349  * @string must be a semi-colon separated list of "&lt;key&gt;=&lt;value&gt;" strings (for example
350  * "DB_NAME=notes;USERNAME=alfred"). Each key and value must respect the RFC 1738 recommendations: the
351  * <constant>&lt;&gt;&quot;#%{}|\^~[]&apos;`;/?:@=&amp;</constant> and space characters are replaced by
352  * <constant>&quot;%%ab&quot;</constant> where
353  * <constant>ab</constant> is the hexadecimal number corresponding to the character (for example the
354  * "DB_NAME=notes;USERNAME=al%%20fred" string will specify a username as "al fred"). If this formalism
355  * is not respected, then some unexpected results may occur.
356  *
357  * Adds new key->value pairs from the given @string. If @cleanup is
358  * set to %TRUE, the previous contents will be discarded before adding
359  * the new pairs.
360  */
361 void
gda_quark_list_add_from_string(GdaQuarkList * qlist,const gchar * string,gboolean cleanup)362 gda_quark_list_add_from_string (GdaQuarkList *qlist, const gchar *string, gboolean cleanup)
363 {
364 	gchar **arr;
365 
366 	g_return_if_fail (qlist != NULL);
367 	if (!string || !*string)
368 		return;
369 
370 	if (cleanup)
371 		gda_quark_list_clear (qlist);
372 
373 	arr = (gchar **) g_strsplit (string, ";", 0);
374 	if (arr) {
375 		gint n = 0;
376 
377 		while (arr[n] && (* (arr[n]))) {
378 			gchar **pair;
379 			gchar *tmp;
380 			for (tmp = arr[n]; *tmp; tmp++) {
381 				if (*tmp == '=')
382 					break;
383 			}
384 			if (!*tmp) {
385 				/* ignore this string since it does not contain the '=' char */
386 				n++;
387 				continue;
388 			}
389 
390 			pair = (gchar **) g_strsplit (arr[n], "=", 2);
391 			if (pair && pair[0]) {
392 				gchar *name = pair[0];
393 				gchar *value = pair[1];
394 				g_strstrip (name);
395 				gda_rfc1738_decode (name);
396 				if (value) {
397 					g_strstrip (value);
398 					gda_rfc1738_decode (value);
399 				}
400 
401 				if (! name_is_protected (name)) {
402 					if (!qlist->hash_table)
403 						qlist->hash_table = g_hash_table_new_full (g_str_hash,
404 											   g_str_equal,
405 											   g_free, g_free);
406 					g_hash_table_insert (qlist->hash_table,
407 							     (gpointer) name, (gpointer) value);
408 				}
409 				else {
410 					ProtectedValue *pvalue;
411 					pvalue = protected_value_new (value);
412 					if (pvalue) {
413 						if (!qlist->hash_protected)
414 							qlist->hash_protected = g_hash_table_new_full (g_str_hash,
415 												       g_str_equal,
416 												       g_free,
417 												       (GDestroyNotify) protected_value_free);
418 						g_hash_table_insert (qlist->hash_protected,
419 								     (gpointer) name, (gpointer) pvalue);
420 						g_free (value); /* has been mangled */
421 					}
422 					else {
423 						if (!qlist->hash_table)
424 							qlist->hash_table = g_hash_table_new_full (g_str_hash,
425 												   g_str_equal,
426 												   g_free, g_free);
427 						g_hash_table_insert (qlist->hash_table,
428 								     (gpointer) name, (gpointer) value);
429 					}
430 				}
431 				g_free (pair);
432 			}
433 			else
434 				g_strfreev (pair);
435 			n++;
436 		}
437 		g_strfreev (arr);
438 	}
439 }
440 
441 /**
442  * gda_quark_list_find:
443  * @qlist: a #GdaQuarkList.
444  * @name: the name of the value to search for.
445  *
446  * Searches for the value identified by @name in the given #GdaQuarkList. For protected values
447  * (authentification data), don't forget to call gda_quark_list_protect_values() when you
448  * don't need them anymore (when needed again, they will be unmangled again).
449  *
450  * Returns: the value associated with the given key if found, or %NULL if not found.
451  */
452 const gchar *
gda_quark_list_find(GdaQuarkList * qlist,const gchar * name)453 gda_quark_list_find (GdaQuarkList *qlist, const gchar *name)
454 {
455 	gchar *value = NULL;
456 	g_return_val_if_fail (qlist, NULL);
457 	g_return_val_if_fail (name, NULL);
458 
459 	if (qlist->hash_table)
460 		value = g_hash_table_lookup (qlist->hash_table, name);
461 	if (value)
462 		return value;
463 
464 	ProtectedValue *pvalue = NULL;
465 	if (qlist->hash_protected)
466 		pvalue = g_hash_table_lookup (qlist->hash_protected, name);
467 	if (pvalue) {
468 		if (! pvalue->cvalue)
469 			protected_value_xor (pvalue, TRUE);
470 		return pvalue->cvalue;
471 	}
472 	else
473 		return NULL;
474 }
475 
476 /**
477  * gda_quark_list_remove:
478  * @qlist: a #GdaQuarkList structure.
479  * @name: an entry name.
480  *
481  * Removes an entry from the #GdaQuarkList, given its name.
482  */
483 void
gda_quark_list_remove(GdaQuarkList * qlist,const gchar * name)484 gda_quark_list_remove (GdaQuarkList *qlist, const gchar *name)
485 {
486 	gboolean removed = FALSE;
487 	g_return_if_fail (qlist != NULL);
488 	g_return_if_fail (name != NULL);
489 
490 	if (qlist->hash_table && g_hash_table_remove (qlist->hash_table, name))
491 		removed = TRUE;
492 	if (!removed && qlist->hash_protected)
493 		g_hash_table_remove (qlist->hash_protected, name);
494 }
495 
496 typedef struct {
497 	gpointer user_data;
498 	GHFunc func;
499 } PFuncData;
500 
501 static void
p_foreach(gchar * key,ProtectedValue * pvalue,PFuncData * data)502 p_foreach (gchar *key, ProtectedValue *pvalue, PFuncData *data)
503 {
504 	if (pvalue->cvalue)
505 		data->func ((gpointer) key, (gpointer) pvalue->cvalue, data->user_data);
506 	else {
507 		protected_value_xor (pvalue, TRUE);
508 		data->func ((gpointer) key, (gpointer) pvalue->cvalue, data->user_data);
509 		protected_value_xor (pvalue, FALSE);
510 	}
511 }
512 
513 /**
514  * gda_quark_list_foreach:
515  * @qlist: a #GdaQuarkList structure.
516  * @func: (scope call): the function to call for each key/value pair
517  * @user_data: (closure): user data to pass to the function
518  *
519  * Calls the given function for each of the key/value pairs in @qlist. The function is passed the key and value
520  * of each pair, and the given user_data parameter. @qlist may not be modified while iterating over it.
521  */
522 void
gda_quark_list_foreach(GdaQuarkList * qlist,GHFunc func,gpointer user_data)523 gda_quark_list_foreach (GdaQuarkList *qlist, GHFunc func, gpointer user_data)
524 {
525 	g_return_if_fail (qlist);
526 
527 	if (qlist->hash_table)
528 		g_hash_table_foreach (qlist->hash_table, func, user_data);
529 	if (qlist->hash_protected) {
530 		PFuncData pdata;
531 		pdata.user_data = user_data;
532 		pdata.func = func;
533 		g_hash_table_foreach (qlist->hash_protected, (GHFunc) p_foreach, &pdata);
534 	}
535 }
536 
537 static void
protect_foreach(G_GNUC_UNUSED gchar * key,ProtectedValue * pvalue,G_GNUC_UNUSED gpointer data)538 protect_foreach (G_GNUC_UNUSED gchar *key, ProtectedValue *pvalue, G_GNUC_UNUSED gpointer data)
539 {
540 	if (pvalue && pvalue->cvalue)
541 		protected_value_xor (pvalue, FALSE);
542 }
543 
544 /**
545  * gda_quark_list_protect_values:
546  * @qlist: a #GdaQuarkList
547  *
548  * Call this function to get rid of the clear version of the value associated to
549  * @name.
550  *
551  * Since: 5.2.0
552  */
553 void
gda_quark_list_protect_values(GdaQuarkList * qlist)554 gda_quark_list_protect_values (GdaQuarkList *qlist)
555 {
556 	g_return_if_fail (qlist);
557 	if (qlist->hash_protected)
558 		g_hash_table_foreach (qlist->hash_protected, (GHFunc) protect_foreach, NULL);
559 }
560