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 "<key>=<value>" strings (for example
241 * "DB_NAME=notes;USERNAME=alfred"). Each key and value must respect the RFC 1738 recommendations: the
242 * <constant><>"#%{}|\^~[]'`;/?:@=&</constant> and space characters are replaced by
243 * <constant>"%%ab"</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 "<key>=<value>" strings (for example
350 * "DB_NAME=notes;USERNAME=alfred"). Each key and value must respect the RFC 1738 recommendations: the
351 * <constant><>"#%{}|\^~[]'`;/?:@=&</constant> and space characters are replaced by
352 * <constant>"%%ab"</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