tls-interaction test: use a weak pointer instead of a deliberate use-after-free
[platform/upstream/glib.git] / gio / tests / tls-interaction.c
1 /* GLib testing framework examples and tests
2  *
3  * Copyright (C) 2011 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Stef Walter <stefw@collobora.co.uk>
21  */
22
23 #include "config.h"
24
25 #include <gio/gio.h>
26
27 typedef struct {
28   /* Class virtual interaction methods */
29   gpointer ask_password_func;
30   gpointer ask_password_async_func;
31   gpointer ask_password_finish_func;
32
33   /* Expected results */
34   GTlsInteractionResult result;
35   GQuark error_domain;
36   gint error_code;
37   const gchar *error_message;
38 } Fixture;
39
40 typedef struct {
41   GTlsInteraction *interaction;
42   GTlsPassword *password;
43   GMainLoop *loop;
44   GThread *interaction_thread;
45   GThread *test_thread;
46   GThread *loop_thread;
47   const Fixture *fixture;
48 } Test;
49
50 typedef struct {
51   GTlsInteraction parent;
52   Test *test;
53 } TestInteraction;
54
55 typedef struct {
56   GTlsInteractionClass parent;
57 } TestInteractionClass;
58
59 static GType test_interaction_get_type (void);
60 G_DEFINE_TYPE (TestInteraction, test_interaction, G_TYPE_TLS_INTERACTION);
61
62 #define TEST_TYPE_INTERACTION         (test_interaction_get_type ())
63 #define TEST_INTERACTION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TEST_TYPE_INTERACTION, TestInteraction))
64 #define TEST_IS_INTERACTION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TEST_TYPE_INTERACTION))
65
66 static void
67 test_interaction_init (TestInteraction *self)
68 {
69
70 }
71
72 static void
73 test_interaction_class_init (TestInteractionClass *klass)
74 {
75   /* By default no virtual methods */
76 }
77
78 static void
79 test_interaction_ask_password_async_success (GTlsInteraction    *interaction,
80                                              GTlsPassword       *password,
81                                              GCancellable       *cancellable,
82                                              GAsyncReadyCallback callback,
83                                              gpointer            user_data)
84 {
85   GSimpleAsyncResult *res;
86   TestInteraction *self;
87
88   g_assert (TEST_IS_INTERACTION (interaction));
89   self = TEST_INTERACTION (interaction);
90
91   g_assert (g_thread_self () == self->test->interaction_thread);
92
93   g_assert (G_IS_TLS_PASSWORD (password));
94   g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
95
96   res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
97                                    test_interaction_ask_password_async_success);
98
99   /* Don't do this in real life. Include a null terminator for testing */
100   g_tls_password_set_value (password, (const guchar *)"the password", 13);
101   g_simple_async_result_complete_in_idle (res);
102   g_object_unref (res);
103 }
104
105
106 static GTlsInteractionResult
107 test_interaction_ask_password_finish_success (GTlsInteraction    *interaction,
108                                               GAsyncResult       *result,
109                                               GError            **error)
110 {
111   TestInteraction *self;
112
113   g_assert (TEST_IS_INTERACTION (interaction));
114   self = TEST_INTERACTION (interaction);
115
116   g_assert (g_thread_self () == self->test->interaction_thread);
117
118   g_assert (g_simple_async_result_is_valid (result, G_OBJECT (interaction),
119                                             test_interaction_ask_password_async_success));
120   g_assert (error != NULL);
121   g_assert (*error == NULL);
122
123   return G_TLS_INTERACTION_HANDLED;
124 }
125
126 static void
127 test_interaction_ask_password_async_failure (GTlsInteraction    *interaction,
128                                              GTlsPassword       *password,
129                                              GCancellable       *cancellable,
130                                              GAsyncReadyCallback callback,
131                                              gpointer            user_data)
132 {
133   GSimpleAsyncResult *res;
134   TestInteraction *self;
135
136   g_assert (TEST_IS_INTERACTION (interaction));
137   self = TEST_INTERACTION (interaction);
138
139   g_assert (g_thread_self () == self->test->interaction_thread);
140
141   g_assert (G_IS_TLS_PASSWORD (password));
142   g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
143
144   res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
145                                    test_interaction_ask_password_async_failure);
146
147   g_simple_async_result_set_error (res, G_FILE_ERROR, G_FILE_ERROR_ACCES, "The message");
148   g_simple_async_result_complete_in_idle (res);
149   g_object_unref (res);
150 }
151
152 static GTlsInteractionResult
153 test_interaction_ask_password_finish_failure (GTlsInteraction    *interaction,
154                                               GAsyncResult       *result,
155                                               GError            **error)
156 {
157   TestInteraction *self;
158
159   g_assert (TEST_IS_INTERACTION (interaction));
160   self = TEST_INTERACTION (interaction);
161
162   g_assert (g_thread_self () == self->test->interaction_thread);
163
164   g_assert (g_simple_async_result_is_valid (result, G_OBJECT (interaction),
165                                             test_interaction_ask_password_async_failure));
166   g_assert (error != NULL);
167   g_assert (*error == NULL);
168
169   if (!g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
170     g_assert_not_reached ();
171   return G_TLS_INTERACTION_FAILED;
172 }
173
174
175 static GTlsInteractionResult
176 test_interaction_ask_password_sync_success (GTlsInteraction    *interaction,
177                                             GTlsPassword       *password,
178                                             GCancellable       *cancellable,
179                                             GError            **error)
180 {
181   TestInteraction *self;
182
183   g_assert (TEST_IS_INTERACTION (interaction));
184   self = TEST_INTERACTION (interaction);
185
186   g_assert (g_thread_self () == self->test->interaction_thread);
187
188   g_assert (G_IS_TLS_PASSWORD (password));
189   g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
190   g_assert (error != NULL);
191   g_assert (*error == NULL);
192
193   /* Don't do this in real life. Include a null terminator for testing */
194   g_tls_password_set_value (password, (const guchar *)"the password", 13);
195   return G_TLS_INTERACTION_HANDLED;
196 }
197
198 static GTlsInteractionResult
199 test_interaction_ask_password_sync_failure (GTlsInteraction    *interaction,
200                                             GTlsPassword       *password,
201                                             GCancellable       *cancellable,
202                                             GError            **error)
203 {
204   TestInteraction *self;
205
206   g_assert (TEST_IS_INTERACTION (interaction));
207   self = TEST_INTERACTION (interaction);
208
209   g_assert (g_thread_self () == self->test->interaction_thread);
210
211   g_assert (G_IS_TLS_PASSWORD (password));
212   g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
213   g_assert (error != NULL);
214   g_assert (*error == NULL);
215
216   g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES, "The message");
217   return G_TLS_INTERACTION_FAILED;
218 }
219
220 /* ----------------------------------------------------------------------------
221  * ACTUAL TESTS
222  */
223
224 static void
225 on_ask_password_async_call (GObject      *source,
226                             GAsyncResult *result,
227                             gpointer      user_data)
228 {
229   Test *test = user_data;
230   GTlsInteractionResult res;
231   GError *error = NULL;
232
233   g_assert (G_IS_TLS_INTERACTION (source));
234   g_assert (G_TLS_INTERACTION (source) == test->interaction);
235
236   /* Check that this callback is being run in the right place */
237   g_assert (g_thread_self () == test->interaction_thread);
238
239   res = g_tls_interaction_ask_password_finish (test->interaction, result,
240                                                &error);
241
242   /* Check that the results match the fixture */
243   g_assert_cmpuint (test->fixture->result, ==, res);
244   switch (test->fixture->result)
245     {
246       case G_TLS_INTERACTION_HANDLED:
247         g_assert_no_error (error);
248         g_assert_cmpstr ((const gchar *)g_tls_password_get_value (test->password, NULL), ==, "the password");
249         break;
250       case G_TLS_INTERACTION_FAILED:
251         g_assert_error (error, test->fixture->error_domain, test->fixture->error_code);
252         g_assert_cmpstr (error->message, ==, test->fixture->error_message);
253         g_clear_error (&error);
254         break;
255       case G_TLS_INTERACTION_UNHANDLED:
256         g_assert_no_error (error);
257         break;
258       default:
259         g_assert_not_reached ();
260     }
261
262   /* Signal the end of the test */
263   g_main_loop_quit (test->loop);
264 }
265
266 static void
267 test_ask_password_async (Test            *test,
268                          gconstpointer    unused)
269 {
270   /* This test only works with a main loop */
271   g_assert (test->loop);
272
273   g_tls_interaction_ask_password_async (test->interaction,
274                                         test->password, NULL,
275                                         on_ask_password_async_call,
276                                         test);
277
278   /* teardown waits until g_main_loop_quit(). called from callback */
279 }
280
281 static void
282 test_invoke_ask_password (Test         *test,
283                           gconstpointer unused)
284 {
285   GTlsInteractionResult res;
286   GError *error = NULL;
287
288   res = g_tls_interaction_invoke_ask_password (test->interaction, test->password,
289                                                NULL, &error);
290
291   /* Check that the results match the fixture */
292   g_assert_cmpuint (test->fixture->result, ==, res);
293   switch (test->fixture->result)
294     {
295       case G_TLS_INTERACTION_HANDLED:
296         g_assert_no_error (error);
297         g_assert_cmpstr ((const gchar *)g_tls_password_get_value (test->password, NULL), ==, "the password");
298         break;
299       case G_TLS_INTERACTION_FAILED:
300         g_assert_error (error, test->fixture->error_domain, test->fixture->error_code);
301         g_assert_cmpstr (error->message, ==, test->fixture->error_message);
302         g_clear_error (&error);
303         break;
304       case G_TLS_INTERACTION_UNHANDLED:
305         g_assert_no_error (error);
306         break;
307       default:
308         g_assert_not_reached ();
309     }
310
311   /* This allows teardown to stop if running with loop */
312   if (test->loop)
313     g_main_loop_quit (test->loop);
314 }
315
316 static void
317 test_ask_password (Test         *test,
318                    gconstpointer unused)
319 {
320   GTlsInteractionResult res;
321   GError *error = NULL;
322
323   res = g_tls_interaction_ask_password (test->interaction, test->password,
324                                         NULL, &error);
325
326   /* Check that the results match the fixture */
327   g_assert_cmpuint (test->fixture->result, ==, res);
328   switch (test->fixture->result)
329     {
330       case G_TLS_INTERACTION_HANDLED:
331         g_assert_no_error (error);
332         g_assert_cmpstr ((const gchar *)g_tls_password_get_value (test->password, NULL), ==, "the password");
333         break;
334       case G_TLS_INTERACTION_FAILED:
335         g_assert_error (error, test->fixture->error_domain, test->fixture->error_code);
336         g_assert_cmpstr (error->message, ==, test->fixture->error_message);
337         g_clear_error (&error);
338         break;
339       case G_TLS_INTERACTION_UNHANDLED:
340         g_assert_no_error (error);
341         break;
342       default:
343         g_assert_not_reached ();
344     }
345
346   /* This allows teardown to stop if running with loop */
347   if (test->loop)
348     g_main_loop_quit (test->loop);
349 }
350
351 /* ----------------------------------------------------------------------------
352  * TEST SETUP
353  */
354
355 static void
356 setup_without_loop (Test           *test,
357                     gconstpointer   user_data)
358 {
359   const Fixture *fixture = user_data;
360   GTlsInteractionClass *klass;
361   test->fixture = fixture;
362
363   test->interaction = g_object_new (TEST_TYPE_INTERACTION, NULL);
364   g_assert (TEST_IS_INTERACTION (test->interaction));
365
366   TEST_INTERACTION (test->interaction)->test = test;
367
368   klass =  G_TLS_INTERACTION_GET_CLASS (test->interaction);
369   klass->ask_password = fixture->ask_password_func;
370   klass->ask_password_async = fixture->ask_password_async_func;
371   klass->ask_password_finish = fixture->ask_password_finish_func;
372
373   test->password = g_tls_password_new (0, "Description");
374   test->test_thread = g_thread_self ();
375
376   /*
377    * If no loop is running then interaction should happen in the same
378    * thread that the tests are running in.
379    */
380   test->interaction_thread = test->test_thread;
381 }
382
383 static void
384 teardown_without_loop (Test            *test,
385                        gconstpointer    unused)
386 {
387   gpointer weak_pointer = test->interaction;
388
389   g_object_add_weak_pointer (weak_pointer, &weak_pointer);
390
391   g_object_unref (test->password);
392
393   g_object_unref (test->interaction);
394
395   g_assert (weak_pointer == NULL);
396
397 }
398
399 typedef struct {
400   GMutex loop_mutex;
401   GCond loop_started;
402   gboolean started;
403   Test *test;
404 } ThreadLoop;
405
406 static gpointer
407 thread_loop (gpointer user_data)
408 {
409   GMainContext *context = g_main_context_default ();
410   ThreadLoop *closure = user_data;
411   Test *test = closure->test;
412
413   g_mutex_lock (&closure->loop_mutex);
414
415   g_assert (test->loop_thread == g_thread_self ());
416   g_assert (test->loop == NULL);
417   test->loop = g_main_loop_new (context, TRUE);
418
419   g_main_context_acquire (context);
420   closure->started = TRUE;
421   g_cond_signal (&closure->loop_started);
422   g_mutex_unlock (&closure->loop_mutex);
423
424   while (g_main_loop_is_running (test->loop))
425     g_main_context_iteration (context, TRUE);
426
427   g_main_context_release (context);
428   return test;
429 }
430
431 static void
432 setup_with_thread_loop (Test            *test,
433                         gconstpointer    user_data)
434 {
435   ThreadLoop closure;
436
437   setup_without_loop (test, user_data);
438
439   g_mutex_init (&closure.loop_mutex);
440   g_cond_init (&closure.loop_started);
441   closure.started = FALSE;
442   closure.test = test;
443
444   g_mutex_lock (&closure.loop_mutex);
445   test->loop_thread = g_thread_new ("loop", thread_loop, &closure);
446   while (!closure.started)
447     g_cond_wait (&closure.loop_started, &closure.loop_mutex);
448   g_mutex_unlock (&closure.loop_mutex);
449
450   /*
451    * When a loop is running then interaction should always occur in the main
452    * context of that loop.
453    */
454   test->interaction_thread = test->loop_thread;
455
456   g_mutex_clear (&closure.loop_mutex);
457   g_cond_clear (&closure.loop_started);
458 }
459
460 static void
461 teardown_with_thread_loop (Test            *test,
462                            gconstpointer    unused)
463 {
464   gpointer check;
465
466   g_assert (test->loop_thread);
467   check = g_thread_join (test->loop_thread);
468   g_assert (check == test);
469   test->loop_thread = NULL;
470
471   g_main_loop_unref (test->loop);
472
473   teardown_without_loop (test, unused);
474 }
475
476 static void
477 setup_with_normal_loop (Test            *test,
478                         gconstpointer    user_data)
479 {
480   GMainContext *context;
481
482   setup_without_loop (test, user_data);
483
484   context = g_main_context_default ();
485   if (!g_main_context_acquire (context))
486     g_assert_not_reached ();
487
488   test->loop = g_main_loop_new (context, TRUE);
489   g_assert (g_main_loop_is_running (test->loop));
490 }
491
492 static void
493 teardown_with_normal_loop (Test            *test,
494                            gconstpointer    unused)
495 {
496   GMainContext *context;
497
498   context = g_main_context_default ();
499   while (g_main_loop_is_running (test->loop))
500     g_main_context_iteration (context, TRUE);
501
502   g_main_context_release (context);
503
504   /* Run test until complete */
505   g_main_loop_unref (test->loop);
506   test->loop = NULL;
507
508   teardown_without_loop (test, unused);
509 }
510
511 typedef void (*TestFunc) (Test *test, gconstpointer data);
512
513 static void
514 test_with_async_ask_password_implementations (const gchar *name,
515                                               TestFunc     setup,
516                                               TestFunc     func,
517                                               TestFunc     teardown,
518                                               GPtrArray   *fixtures)
519 {
520   gchar *test_name;
521   Fixture *fixture;
522
523   /* Async implementation that succeeds */
524   fixture = g_new0 (Fixture, 1);
525   fixture->ask_password_async_func = test_interaction_ask_password_async_success;
526   fixture->ask_password_finish_func = test_interaction_ask_password_finish_success;
527   fixture->ask_password_func = NULL;
528   fixture->result = G_TLS_INTERACTION_HANDLED;
529   test_name = g_strdup_printf ("%s/async-implementation-success", name);
530   g_test_add (test_name, Test, fixture, setup, func, teardown);
531   g_free (test_name);
532   g_ptr_array_add (fixtures, fixture);
533
534   /* Async implementation that fails */
535   fixture = g_new0 (Fixture, 1);
536   fixture->ask_password_async_func = test_interaction_ask_password_async_failure;
537   fixture->ask_password_finish_func = test_interaction_ask_password_finish_failure;
538   fixture->ask_password_func = NULL;
539   fixture->result = G_TLS_INTERACTION_FAILED;
540   fixture->error_domain = G_FILE_ERROR;
541   fixture->error_code = G_FILE_ERROR_ACCES;
542   fixture->error_message = "The message";
543   test_name = g_strdup_printf ("%s/async-implementation-failure", name);
544   g_test_add (test_name, Test, fixture, setup, func, teardown);
545   g_free (test_name);
546   g_ptr_array_add (fixtures, fixture);
547 }
548 static void
549 test_with_unhandled_ask_password_implementations (const gchar *name,
550                                                   TestFunc     setup,
551                                                   TestFunc     func,
552                                                   TestFunc     teardown,
553                                                   GPtrArray   *fixtures)
554 {
555   gchar *test_name;
556   Fixture *fixture;
557
558   /* Unhandled implementation */
559   fixture = g_new0 (Fixture, 1);
560   fixture->ask_password_async_func = NULL;
561   fixture->ask_password_finish_func = NULL;
562   fixture->ask_password_func = NULL;
563   fixture->result = G_TLS_INTERACTION_UNHANDLED;
564   test_name = g_strdup_printf ("%s/unhandled-implementation", name);
565   g_test_add (test_name, Test, fixture, setup, func, teardown);
566   g_free (test_name);
567   g_ptr_array_add (fixtures, fixture);
568 }
569
570 static void
571 test_with_sync_ask_password_implementations (const gchar *name,
572                                              TestFunc     setup,
573                                              TestFunc     func,
574                                              TestFunc     teardown,
575                                              GPtrArray   *fixtures)
576 {
577   gchar *test_name;
578   Fixture *fixture;
579
580   /* Sync implementation that succeeds */
581   fixture = g_new0 (Fixture, 1);
582   fixture->ask_password_async_func = NULL;
583   fixture->ask_password_finish_func = NULL;
584   fixture->ask_password_func = test_interaction_ask_password_sync_success;
585   fixture->result = G_TLS_INTERACTION_HANDLED;
586   test_name = g_strdup_printf ("%s/sync-implementation-success", name);
587   g_test_add (test_name, Test, fixture, setup, func, teardown);
588   g_free (test_name);
589   g_ptr_array_add (fixtures, fixture);
590
591   /* Async implementation that fails */
592   fixture = g_new0 (Fixture, 1);
593   fixture->ask_password_async_func = NULL;
594   fixture->ask_password_finish_func = NULL;
595   fixture->ask_password_func = test_interaction_ask_password_sync_failure;
596   fixture->result = G_TLS_INTERACTION_FAILED;
597   fixture->error_domain = G_FILE_ERROR;
598   fixture->error_code = G_FILE_ERROR_ACCES;
599   fixture->error_message = "The message";
600   test_name = g_strdup_printf ("%s/sync-implementation-failure", name);
601   g_test_add (test_name, Test, fixture, setup, func, teardown);
602   g_free (test_name);
603   g_ptr_array_add (fixtures, fixture);
604 }
605
606 int
607 main (int   argc,
608       char *argv[])
609 {
610   GPtrArray *fixtures;
611   gint ret;
612
613   g_type_init ();
614   g_test_init (&argc, &argv, NULL);
615
616   fixtures = g_ptr_array_new_with_free_func (g_free);
617
618   /* Tests for g_tls_interaction_invoke_ask_password */
619
620   test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/invoke-with-loop",
621                                                     setup_with_thread_loop, test_invoke_ask_password,
622                                                     teardown_with_thread_loop, fixtures);
623   test_with_async_ask_password_implementations ("/tls-interaction/ask-password/invoke-with-loop",
624                                                 setup_with_thread_loop, test_invoke_ask_password,
625                                                 teardown_with_thread_loop, fixtures);
626   test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/invoke-with-loop",
627                                                setup_with_thread_loop, test_invoke_ask_password,
628                                                teardown_with_thread_loop, fixtures);
629
630   test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/invoke-without-loop",
631                                                     setup_without_loop, test_invoke_ask_password,
632                                                     teardown_without_loop, fixtures);
633   test_with_async_ask_password_implementations ("/tls-interaction/ask-password/invoke-without-loop",
634                                                 setup_without_loop, test_invoke_ask_password,
635                                                 teardown_without_loop, fixtures);
636   test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/invoke-without-loop",
637                                                setup_without_loop, test_invoke_ask_password,
638                                                teardown_without_loop, fixtures);
639
640   test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/invoke-in-loop",
641                                                     setup_with_normal_loop, test_invoke_ask_password,
642                                                     teardown_with_normal_loop, fixtures);
643   test_with_async_ask_password_implementations ("/tls-interaction/ask-password/invoke-in-loop",
644                                                 setup_with_normal_loop, test_invoke_ask_password,
645                                                 teardown_with_normal_loop, fixtures);
646   test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/invoke-in-loop",
647                                                setup_with_normal_loop, test_invoke_ask_password,
648                                                teardown_with_normal_loop, fixtures);
649
650   /* Tests for g_tls_interaction_ask_password */
651   test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/sync",
652                                                     setup_without_loop, test_ask_password,
653                                                     teardown_without_loop, fixtures);
654   test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/sync",
655                                                setup_without_loop, test_ask_password,
656                                                teardown_without_loop, fixtures);
657
658   /* Tests for g_tls_interaction_ask_password_async */
659   test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/async",
660                                                     setup_with_normal_loop, test_ask_password_async,
661                                                     teardown_with_normal_loop, fixtures);
662   test_with_async_ask_password_implementations ("/tls-interaction/ask-password/async",
663                                                 setup_with_normal_loop, test_ask_password_async,
664                                                 teardown_with_normal_loop, fixtures);
665
666   ret = g_test_run();
667   g_ptr_array_free (fixtures, TRUE);
668   return ret;
669 }