Line data Source code
1 : /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 : /* unit-test-file-store.c: Test file store functionality
3 :
4 : Copyright (C) 2008 Stefan Walter
5 :
6 : The Gnome Keyring Library is free software; you can redistribute it and/or
7 : modify it under the terms of the GNU Library General Public License as
8 : published by the Free Software Foundation; either version 2 of the
9 : License, or (at your option) any later version.
10 :
11 : The Gnome Keyring Library is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : Library General Public License for more details.
15 :
16 : You should have received a copy of the GNU Library General Public
17 : License along with the Gnome Library; see the file COPYING.LIB. If not,
18 : <http://www.gnu.org/licenses/>.
19 :
20 : Author: Stef Walter <stef@memberwebs.com>
21 : */
22 :
23 : #include "config.h"
24 :
25 : #include "mock-gnome2-module.h"
26 :
27 : #include "gnome2-store/gkm-gnome2-storage.h"
28 :
29 : #include "gkm/gkm-certificate.h"
30 : #include "gkm/gkm-module.h"
31 : #include "gkm/gkm-serializable.h"
32 : #include "gkm/gkm-test.h"
33 :
34 : #include "egg/egg-libgcrypt.h"
35 : #include "egg/egg-testing.h"
36 :
37 : #include <glib/gstdio.h>
38 :
39 : #include <sys/types.h>
40 : #include <sys/wait.h>
41 : #include <fcntl.h>
42 : #include <stdlib.h>
43 : #include <string.h>
44 : #include <unistd.h>
45 :
46 : typedef struct {
47 : gchar *directory;
48 : GkmModule *module;
49 : GkmGnome2Storage *storage;
50 : GkmObject *new_object;
51 : gchar *new_filename;
52 : GkmObject *old_object;
53 : } Test;
54 :
55 : #define MSEC(x) ((x) * 1000)
56 :
57 : static void
58 4 : setup_directory (Test *test,
59 : gconstpointer unused)
60 : {
61 4 : test->directory = egg_tests_create_scratch_directory (
62 : SRCDIR "/pkcs11/gnome2-store/fixtures/Thawte_Personal_Premium_CA.cer",
63 : SRCDIR "/pkcs11/gnome2-store/fixtures/user.keystore",
64 : NULL);
65 4 : }
66 :
67 : static void
68 4 : setup_module (Test *test,
69 : gconstpointer unused)
70 : {
71 4 : CK_ATTRIBUTE url = { CKA_URL, NULL, 0 };
72 : gchar *contents;
73 : gsize length;
74 : GBytes *bytes;
75 : GkmManager *manager;
76 4 : GError *error = NULL;
77 : GkmSession *session;
78 : CK_ATTRIBUTE attr;
79 : CK_OBJECT_CLASS klass;
80 : CK_RV rv;
81 :
82 4 : g_assert (test->directory != NULL);
83 :
84 4 : test->module = mock_gnome2_module_initialize_and_enter ();
85 4 : manager = gkm_module_get_manager (test->module);
86 4 : session = mock_gnome2_module_open_session (TRUE);
87 :
88 4 : test->storage = gkm_gnome2_storage_new (test->module, test->directory);
89 4 : rv = gkm_gnome2_storage_refresh (test->storage);
90 4 : gkm_assert_cmprv (rv, ==, CKR_OK);
91 4 : g_object_add_weak_pointer (G_OBJECT (test->storage), (gpointer *)&test->storage);
92 :
93 : /* We already have the CKA_LABEL attribute */
94 4 : gkm_store_register_schema (GKM_STORE (test->storage), &url, NULL, 0);
95 :
96 : /*
97 : * Create a new object that hasn't yet been stored in the storage.
98 : * It's a certificate because that's easiest.
99 : */
100 4 : test->new_object = g_object_new (GKM_TYPE_CERTIFICATE,
101 : "unique", "test.cer",
102 : "module", test->module,
103 : "manager", manager,
104 : NULL);
105 4 : g_file_get_contents (SRCDIR "/pkcs11/gnome2-store/fixtures/test-certificate.cer", &contents, &length, &error);
106 4 : g_assert_no_error (error);
107 :
108 4 : bytes = g_bytes_new_take (contents, length);
109 4 : if (!gkm_serializable_load (GKM_SERIALIZABLE (test->new_object), NULL, bytes))
110 0 : g_assert_not_reached ();
111 4 : g_bytes_unref (bytes);
112 :
113 : /* We happen to know this certificate will get named */
114 4 : test->new_filename = g_build_filename (test->directory, "CA_Cert_Signing_Authority.cer", NULL);
115 :
116 : /*
117 : * Find the object stored in the storage, it's a certificate, and we happen to
118 : * know there's only one
119 : */
120 4 : klass = CKO_CERTIFICATE;
121 4 : attr.type = CKA_CLASS;
122 4 : attr.pValue = &klass;
123 4 : attr.ulValueLen = sizeof (klass);
124 4 : test->old_object = gkm_manager_find_one_by_attributes (manager, session, &attr, 1);
125 4 : g_assert (GKM_IS_OBJECT (test->old_object));
126 4 : g_object_ref (test->old_object);
127 4 : }
128 :
129 : static void
130 4 : setup_all (Test *test,
131 : gconstpointer unused)
132 : {
133 4 : setup_directory (test, unused);
134 4 : setup_module (test, unused);
135 4 : }
136 :
137 : static void
138 4 : teardown_directory (Test *test,
139 : gconstpointer unused)
140 : {
141 4 : egg_tests_remove_scratch_directory (test->directory);
142 4 : g_free (test->directory);
143 4 : }
144 :
145 : static void
146 4 : teardown_module (Test *test,
147 : gconstpointer unused)
148 : {
149 4 : g_assert (test->directory);
150 :
151 4 : g_object_unref (test->new_object);
152 4 : g_object_unref (test->old_object);
153 4 : g_object_unref (test->storage);
154 4 : g_assert (test->storage == NULL);
155 :
156 4 : mock_gnome2_module_leave_and_finalize ();
157 :
158 4 : g_free (test->new_filename);
159 4 : }
160 :
161 : static void
162 4 : teardown_all (Test *test,
163 : gconstpointer unused)
164 : {
165 4 : teardown_module (test, unused);
166 4 : teardown_directory (test, unused);
167 4 : }
168 :
169 : static void
170 1 : test_create (Test *test,
171 : gconstpointer unused)
172 : {
173 : GkmTransaction *transaction;
174 :
175 1 : transaction = gkm_transaction_new ();
176 :
177 1 : gkm_gnome2_storage_create (test->storage, transaction, test->new_object);
178 1 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
179 :
180 1 : gkm_transaction_complete_and_unref (transaction);
181 :
182 1 : g_assert (g_file_test (test->new_filename, G_FILE_TEST_EXISTS));
183 1 : }
184 :
185 : static void
186 1 : test_create_and_fail (Test *test,
187 : gconstpointer unused)
188 : {
189 : GkmTransaction *transaction;
190 :
191 1 : transaction = gkm_transaction_new ();
192 :
193 1 : gkm_gnome2_storage_create (test->storage, transaction, test->new_object);
194 1 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
195 :
196 1 : gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED);
197 1 : gkm_transaction_complete_and_unref (transaction);
198 :
199 1 : g_assert (!g_file_test (test->new_filename, G_FILE_TEST_EXISTS));
200 1 : }
201 :
202 : static void
203 1 : test_write_value (Test *test,
204 : gconstpointer unused)
205 : {
206 1 : CK_ATTRIBUTE label = { CKA_LABEL, "Hello", 5 };
207 1 : CK_ATTRIBUTE url = { CKA_URL, "http://example.com", 18 };
208 : GkmTransaction *transaction;
209 : gchar *string;
210 :
211 1 : transaction = gkm_transaction_new ();
212 :
213 1 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
214 : test->old_object, &label);
215 1 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
216 :
217 1 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
218 : test->old_object, &url);
219 1 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
220 :
221 1 : gkm_transaction_complete_and_unref (transaction);
222 :
223 1 : string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_URL);
224 1 : g_assert_cmpstr (string, ==, "http://example.com");
225 1 : g_free (string);
226 :
227 1 : string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_LABEL);
228 1 : g_assert_cmpstr (string, ==, "Hello");
229 1 : g_free (string);
230 :
231 1 : }
232 :
233 : static void
234 0 : test_locking_transaction (Test *test,
235 : gconstpointer unused)
236 : {
237 0 : guint iterations = 30;
238 : guint i;
239 : pid_t pid;
240 :
241 : /* Fork before setting up the model, as it may start threads */
242 0 : pid = fork ();
243 0 : g_assert (pid >= 0);
244 :
245 : /*
246 : * This is the child. It initializes, writes a value, waits 100 ms,
247 : * writes a second value, and then writes another value.
248 : */
249 0 : if (pid == 0) {
250 : CK_ATTRIBUTE attr;
251 : GkmTransaction *transaction;
252 : gchar *string;
253 :
254 0 : setup_module (test, unused);
255 :
256 0 : for (i = 0; i < iterations; i++) {
257 0 : g_printerr ("c");
258 :
259 0 : transaction = gkm_transaction_new ();
260 :
261 0 : string = g_strdup_printf ("%d", i);
262 :
263 0 : attr.type = CKA_LABEL;
264 0 : attr.pValue = string;
265 0 : attr.ulValueLen = strlen (string);
266 :
267 0 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
268 : test->old_object, &attr);
269 0 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
270 :
271 0 : g_usleep (100 * 1000);
272 :
273 0 : attr.type = CKA_URL;
274 0 : attr.pValue = string;
275 0 : attr.ulValueLen = strlen (string);
276 :
277 0 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
278 : test->old_object, &attr);
279 0 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
280 :
281 0 : g_free (string);
282 :
283 0 : gkm_transaction_complete_and_unref (transaction);
284 :
285 0 : g_usleep (10 * 1000);
286 : }
287 :
288 0 : teardown_module (test, unused);
289 0 : _exit (0);
290 : g_assert_not_reached ();
291 :
292 : /*
293 : * This is the parent. it initializes, waits 100 ms, writes a value that
294 : * should override the one from the child, because the file is locked
295 : * when it tries to write, so it waits for the child to finish. The other
296 : * attribute from the child (the label) should come through.
297 : */
298 : } else {
299 : gchar *string1;
300 : gchar *string2;
301 : pid_t wpid;
302 : int status;
303 : CK_RV rv;
304 :
305 0 : g_assert (pid != -1);
306 :
307 0 : setup_module (test, unused);
308 :
309 0 : for (i = 0; i < iterations; i++) {
310 0 : g_printerr ("p");
311 :
312 0 : string1 = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_URL);
313 :
314 0 : g_usleep (g_random_int_range (1, 200) * 1000);
315 :
316 0 : string2 = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_LABEL);
317 :
318 0 : g_assert_cmpstr (string1, ==, string2);
319 0 : g_free (string1);
320 0 : g_free (string2);
321 :
322 0 : rv = gkm_gnome2_storage_refresh (test->storage);
323 0 : gkm_assert_cmprv (rv, ==, CKR_OK);
324 : }
325 :
326 : /* wait for the child to finish */
327 0 : wpid = waitpid (pid, &status, 0);
328 0 : g_assert_cmpint (wpid, ==, pid);
329 0 : g_assert_cmpint (status, ==, 0);
330 :
331 0 : teardown_module (test, unused);
332 : }
333 0 : }
334 :
335 : static void
336 0 : test_lock_writes (Test *test,
337 : gconstpointer unused)
338 : {
339 : pid_t pid;
340 :
341 : /* Fork before setting up the model, as it may start threads */
342 0 : pid = fork ();
343 0 : g_assert (pid >= 0);
344 :
345 : /*
346 : * This is the child. It initializes, writes a value, waits 100 ms,
347 : * writes a second value, and then writes another value.
348 : */
349 0 : if (pid == 0) {
350 0 : CK_ATTRIBUTE label = { CKA_LABEL, "Hello from child", 16 };
351 0 : CK_ATTRIBUTE url = { CKA_URL, "http://child.example.com", 24 };
352 : GkmTransaction *transaction;
353 :
354 0 : setup_module (test, unused);
355 :
356 0 : transaction = gkm_transaction_new ();
357 :
358 0 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
359 : test->old_object, &label);
360 0 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
361 :
362 0 : g_usleep (MSEC (100));
363 :
364 0 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
365 : test->old_object, &url);
366 0 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
367 :
368 0 : gkm_transaction_complete_and_unref (transaction);
369 :
370 0 : teardown_module (test, unused);
371 0 : _exit (0);
372 : g_assert_not_reached ();
373 :
374 : /*
375 : * This is the parent. it initializes, waits 100 ms, writes a value that
376 : * should override the one from the child, because the file is locked
377 : * when it tries to write, so it waits for the child to finish. The other
378 : * attribute from the child (the label) should come through.
379 : */
380 : } else {
381 0 : CK_ATTRIBUTE url = { CKA_URL, "http://parent.example.com", 25 };
382 : GkmTransaction *transaction;
383 : gchar *string;
384 : pid_t wpid;
385 : int status;
386 : CK_RV rv;
387 :
388 0 : g_assert (pid != -1);
389 :
390 0 : setup_module (test, unused);
391 :
392 : /* Refresh the store, and check values are not set */
393 0 : string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_URL);
394 0 : g_assert (string == NULL);
395 :
396 0 : string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_LABEL);
397 0 : g_assert (string == NULL);
398 :
399 0 : g_usleep (MSEC (1000));
400 :
401 0 : transaction = gkm_transaction_new ();
402 :
403 0 : gkm_store_write_value (GKM_STORE (test->storage), transaction,
404 : test->old_object, &url);
405 0 : gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
406 :
407 : /* wait for the child to finish */
408 0 : wpid = waitpid (pid, &status, 0);
409 0 : g_assert_cmpint (wpid, ==, pid);
410 0 : g_assert_cmpint (status, ==, 0);
411 :
412 0 : gkm_transaction_complete_and_unref (transaction);
413 :
414 0 : g_usleep (MSEC (1000));
415 :
416 0 : rv = gkm_gnome2_storage_refresh (test->storage);
417 0 : gkm_assert_cmprv (rv, ==, CKR_OK);
418 :
419 0 : string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_URL);
420 0 : g_assert_cmpstr (string, ==, "http://parent.example.com");
421 0 : g_free (string);
422 :
423 0 : string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_LABEL);
424 0 : g_assert_cmpstr (string, ==, "Hello from child");
425 0 : g_free (string);
426 :
427 0 : teardown_module (test, unused);
428 : }
429 0 : }
430 :
431 : static void
432 1 : test_relock (Test *test,
433 : gconstpointer unused)
434 : {
435 : GkmTransaction *transaction;
436 : GkmSecret *old_login;
437 : GkmSecret *new_login;
438 :
439 1 : transaction = gkm_transaction_new ();
440 :
441 1 : old_login = NULL;
442 1 : new_login = gkm_secret_new_from_password ("blah");
443 :
444 1 : gkm_gnome2_storage_relock (test->storage, transaction, old_login, new_login);
445 1 : gkm_assert_cmprv (gkm_transaction_complete_and_unref (transaction), ==, CKR_OK);
446 :
447 1 : g_object_unref (new_login);
448 1 : }
449 :
450 : static void
451 0 : null_log_handler (const gchar *log_domain,
452 : GLogLevelFlags log_level,
453 : const gchar *message,
454 : gpointer user_data)
455 : {
456 :
457 0 : }
458 :
459 : int
460 1 : main (int argc, char **argv)
461 : {
462 : #if !GLIB_CHECK_VERSION(2,35,0)
463 : g_type_init ();
464 : #endif
465 1 : g_test_init (&argc, &argv, NULL);
466 :
467 1 : egg_libgcrypt_initialize ();
468 :
469 : /* Suppress these messages in tests */
470 1 : g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE,
471 : null_log_handler, NULL);
472 :
473 1 : g_test_add ("/gnome2-store/storage/create", Test, NULL,
474 : setup_all, test_create, teardown_all);
475 1 : g_test_add ("/gnome2-store/storage/create_and_fail", Test, NULL,
476 : setup_all, test_create_and_fail, teardown_all);
477 1 : g_test_add ("/gnome2-store/storage/write_value", Test, NULL,
478 : setup_all, test_write_value, teardown_all);
479 1 : g_test_add ("/gnome2-store/storage/relock", Test, NULL,
480 : setup_all, test_relock, teardown_all);
481 :
482 1 : if (!g_test_quick ()) {
483 0 : g_test_add ("/gnome2-store/storage/locking_transaction", Test, NULL,
484 : setup_directory, test_locking_transaction, teardown_directory);
485 0 : g_test_add ("/gnome2-store/storage/lock_writes", Test, NULL,
486 : setup_directory, test_lock_writes, teardown_directory);
487 : }
488 :
489 1 : return g_test_run ();
490 : }
|