1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 #include "test-utils.h"
4
5 SoupURI *uri;
6 GTlsDatabase *null_tlsdb;
7
8 static void
do_properties_test_for_session(SoupSession * session)9 do_properties_test_for_session (SoupSession *session)
10 {
11 SoupMessage *msg;
12 GTlsCertificate *cert;
13 GTlsCertificateFlags flags;
14
15 msg = soup_message_new_from_uri ("GET", uri);
16 soup_session_send_message (session, msg);
17 soup_test_assert_message_status (msg, SOUP_STATUS_OK);
18
19 if (soup_message_get_https_status (msg, &cert, &flags)) {
20 g_assert_true (G_IS_TLS_CERTIFICATE (cert));
21 g_assert_cmpuint (flags, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
22 } else
23 soup_test_assert (FALSE, "Response not https");
24
25 g_test_bug ("665182");
26 g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
27
28 g_object_unref (msg);
29 }
30
31 static void
do_async_properties_tests(void)32 do_async_properties_tests (void)
33 {
34 SoupSession *session;
35
36 SOUP_TEST_SKIP_IF_NO_TLS;
37
38 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
39 g_object_set (G_OBJECT (session),
40 SOUP_SESSION_TLS_DATABASE, null_tlsdb,
41 SOUP_SESSION_SSL_STRICT, FALSE,
42 NULL);
43 do_properties_test_for_session (session);
44 soup_test_session_abort_unref (session);
45 }
46
47 static void
do_sync_properties_tests(void)48 do_sync_properties_tests (void)
49 {
50 SoupSession *session;
51
52 SOUP_TEST_SKIP_IF_NO_TLS;
53
54 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
55 g_object_set (G_OBJECT (session),
56 SOUP_SESSION_TLS_DATABASE, null_tlsdb,
57 SOUP_SESSION_SSL_STRICT, FALSE,
58 NULL);
59 do_properties_test_for_session (session);
60 soup_test_session_abort_unref (session);
61 }
62
63 typedef struct {
64 const char *name;
65 gboolean sync;
66 gboolean strict;
67 gboolean with_ca_list;
68 guint expected_status;
69 } StrictnessTest;
70
71 static const StrictnessTest strictness_tests[] = {
72 { "/ssl/strictness/async/strict/with-ca",
73 FALSE, TRUE, TRUE, SOUP_STATUS_OK },
74 { "/ssl/strictness/async/strict/without-ca",
75 FALSE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED },
76 { "/ssl/strictness/async/non-strict/with-ca",
77 FALSE, FALSE, TRUE, SOUP_STATUS_OK },
78 { "/ssl/strictness/async/non-strict/without-ca",
79 FALSE, FALSE, FALSE, SOUP_STATUS_OK },
80 { "/ssl/strictness/sync/strict/with-ca",
81 TRUE, TRUE, TRUE, SOUP_STATUS_OK },
82 { "/ssl/strictness/sync/strict/without-ca",
83 TRUE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED },
84 { "/ssl/strictness/sync/non-strict/with-ca",
85 TRUE, FALSE, TRUE, SOUP_STATUS_OK },
86 { "/ssl/strictness/sync/non-strict/without-ca",
87 TRUE, FALSE, FALSE, SOUP_STATUS_OK },
88 };
89
90 static void
do_strictness_test(gconstpointer data)91 do_strictness_test (gconstpointer data)
92 {
93 const StrictnessTest *test = data;
94 SoupSession *session;
95 SoupMessage *msg;
96 GTlsCertificateFlags flags = 0;
97
98 SOUP_TEST_SKIP_IF_NO_TLS;
99
100 session = soup_test_session_new (test->sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
101 NULL);
102 if (!test->strict) {
103 g_object_set (G_OBJECT (session),
104 SOUP_SESSION_SSL_STRICT, FALSE,
105 NULL);
106 }
107 if (!test->with_ca_list) {
108 g_object_set (G_OBJECT (session),
109 SOUP_SESSION_TLS_DATABASE, null_tlsdb,
110 NULL);
111 }
112
113 msg = soup_message_new_from_uri ("GET", uri);
114 soup_session_send_message (session, msg);
115 soup_test_assert_message_status (msg, test->expected_status);
116
117 g_test_bug ("690176");
118 g_assert_true (soup_message_get_https_status (msg, NULL, &flags));
119
120 g_test_bug ("665182");
121 if (test->with_ca_list && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
122 g_assert_true (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
123 else
124 g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
125
126 if (msg->status_code == SOUP_STATUS_SSL_FAILED &&
127 test->expected_status != SOUP_STATUS_SSL_FAILED)
128 debug_printf (1, " tls error flags: 0x%x\n", flags);
129
130 g_object_unref (msg);
131
132 soup_test_session_abort_unref (session);
133 }
134
135 static void
property_changed(GObject * object,GParamSpec * param,gpointer user_data)136 property_changed (GObject *object, GParamSpec *param, gpointer user_data)
137 {
138 gboolean *changed = user_data;
139
140 *changed = TRUE;
141 }
142
143 static void
do_session_property_tests(void)144 do_session_property_tests (void)
145 {
146 gboolean use_system_changed, tlsdb_changed, ca_file_changed;
147 gboolean use_system;
148 GTlsDatabase *tlsdb;
149 char *ca_file;
150 SoupSession *session;
151 GParamSpec *pspec;
152
153 g_test_bug ("673678");
154
155 SOUP_TEST_SKIP_IF_NO_TLS;
156
157 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
158 session = soup_session_async_new ();
159 G_GNUC_END_IGNORE_DEPRECATIONS;
160
161 /* Temporarily undeprecate SOUP_SESSION_SSL_CA_FILE to avoid warnings. */
162 pspec = g_object_class_find_property (g_type_class_peek (SOUP_TYPE_SESSION),
163 SOUP_SESSION_SSL_CA_FILE);
164 pspec->flags &= ~G_PARAM_DEPRECATED;
165
166 g_signal_connect (session, "notify::ssl-use-system-ca-file",
167 G_CALLBACK (property_changed), &use_system_changed);
168 g_signal_connect (session, "notify::tls-database",
169 G_CALLBACK (property_changed), &tlsdb_changed);
170 g_signal_connect (session, "notify::ssl-ca-file",
171 G_CALLBACK (property_changed), &ca_file_changed);
172
173 g_object_get (G_OBJECT (session),
174 "ssl-use-system-ca-file", &use_system,
175 "tls-database", &tlsdb,
176 "ssl-ca-file", &ca_file,
177 NULL);
178 soup_test_assert (!use_system,
179 "ssl-use-system-ca-file defaults to TRUE");
180 soup_test_assert (tlsdb == NULL,
181 "tls-database set by default");
182 soup_test_assert (ca_file == NULL,
183 "ca-file set by default");
184
185 use_system_changed = tlsdb_changed = ca_file_changed = FALSE;
186 g_object_set (G_OBJECT (session),
187 "ssl-use-system-ca-file", TRUE,
188 NULL);
189 g_object_get (G_OBJECT (session),
190 "ssl-use-system-ca-file", &use_system,
191 "tls-database", &tlsdb,
192 "ssl-ca-file", &ca_file,
193 NULL);
194 soup_test_assert (use_system,
195 "setting ssl-use-system-ca-file failed");
196 g_assert_true (use_system_changed);
197 soup_test_assert (tlsdb != NULL,
198 "setting ssl-use-system-ca-file didn't set tls-database");
199 g_assert_true (tlsdb_changed);
200 g_clear_object (&tlsdb);
201 soup_test_assert (ca_file == NULL,
202 "setting ssl-use-system-ca-file set ssl-ca-file");
203 g_assert_false (ca_file_changed);
204
205 use_system_changed = tlsdb_changed = ca_file_changed = FALSE;
206 g_object_set (G_OBJECT (session),
207 "ssl-ca-file", g_test_get_filename (G_TEST_DIST, "/test-cert.pem", NULL),
208 NULL);
209 g_object_get (G_OBJECT (session),
210 "ssl-use-system-ca-file", &use_system,
211 "tls-database", &tlsdb,
212 "ssl-ca-file", &ca_file,
213 NULL);
214 soup_test_assert (!use_system,
215 "setting ssl-ca-file left ssl-use-system-ca-file set");
216 g_assert_true (use_system_changed);
217 soup_test_assert (tlsdb != NULL,
218 "setting ssl-ca-file didn't set tls-database");
219 g_assert_true (tlsdb_changed);
220 g_clear_object (&tlsdb);
221 soup_test_assert (ca_file != NULL,
222 "setting ssl-ca-file failed");
223 g_assert_true (ca_file_changed);
224 g_free (ca_file);
225
226 use_system_changed = tlsdb_changed = ca_file_changed = FALSE;
227 g_object_set (G_OBJECT (session),
228 "tls-database", NULL,
229 NULL);
230 g_object_get (G_OBJECT (session),
231 "ssl-use-system-ca-file", &use_system,
232 "tls-database", &tlsdb,
233 "ssl-ca-file", &ca_file,
234 NULL);
235 soup_test_assert (!use_system,
236 "setting tls-database NULL left ssl-use-system-ca-file set");
237 g_assert_false (use_system_changed);
238 soup_test_assert (tlsdb == NULL,
239 "setting tls-database NULL failed");
240 g_assert_true (tlsdb_changed);
241 soup_test_assert (ca_file == NULL,
242 "setting tls-database didn't clear ssl-ca-file");
243 g_assert_true (ca_file_changed);
244
245 soup_test_session_abort_unref (session);
246
247 /* Re-deprecate SOUP_SESSION_SSL_CA_FILE */
248 pspec->flags |= G_PARAM_DEPRECATED;
249 }
250
251 /* GTlsInteraction subclass for do_interaction_test */
252 typedef GTlsInteraction TestTlsInteraction;
253 typedef GTlsInteractionClass TestTlsInteractionClass;
254
255 GType test_tls_interaction_get_type (void);
256
257 G_DEFINE_TYPE (TestTlsInteraction, test_tls_interaction, G_TYPE_TLS_INTERACTION);
258
259 static void
test_tls_interaction_init(TestTlsInteraction * interaction)260 test_tls_interaction_init (TestTlsInteraction *interaction)
261 {
262
263 }
264
265 static GTlsInteractionResult
test_tls_interaction_request_certificate(GTlsInteraction * interaction,GTlsConnection * connection,GTlsCertificateRequestFlags flags,GCancellable * cancellable,GError ** error)266 test_tls_interaction_request_certificate (GTlsInteraction *interaction,
267 GTlsConnection *connection,
268 GTlsCertificateRequestFlags flags,
269 GCancellable *cancellable,
270 GError **error)
271 {
272 GTlsCertificate *cert;
273 const char *ssl_cert_file, *ssl_key_file;
274 GError *my_error = NULL;
275
276 /* Yes, we use the same certificate for the client as for the server. Shrug */
277 ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL);
278 ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL);
279 cert = g_tls_certificate_new_from_files (ssl_cert_file,
280 ssl_key_file,
281 &my_error);
282 g_assert_no_error (my_error);
283
284 g_tls_connection_set_certificate (connection, cert);
285 g_object_unref (cert);
286
287 return G_TLS_INTERACTION_HANDLED;
288 }
289
290 static void
test_tls_interaction_class_init(TestTlsInteractionClass * klass)291 test_tls_interaction_class_init (TestTlsInteractionClass *klass)
292 {
293 GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
294
295 interaction_class->request_certificate = test_tls_interaction_request_certificate;
296 }
297
298
299 #define INTERACTION_TEST_HTTP_RESPONSE "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nOK\r\n"
300
301 static gboolean
accept_client_certificate(GTlsConnection * server,GTlsCertificate * client_cert,GTlsCertificateFlags errors)302 accept_client_certificate (GTlsConnection *server,
303 GTlsCertificate *client_cert,
304 GTlsCertificateFlags errors)
305 {
306 return TRUE;
307 }
308
309 static void
got_connection(GThreadedSocketService * service,GSocketConnection * connection,GObject * source_object)310 got_connection (GThreadedSocketService *service,
311 GSocketConnection *connection,
312 GObject *source_object)
313 {
314 GIOStream *tls;
315 GTlsCertificate *server_cert;
316 GError *error = NULL;
317 const char *ssl_cert_file, *ssl_key_file;
318 GMainContext *thread_context;
319
320 thread_context = g_main_context_new ();
321 g_main_context_push_thread_default (thread_context);
322
323 ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL);
324 ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL);
325 server_cert = g_tls_certificate_new_from_files (ssl_cert_file,
326 ssl_key_file,
327 &error);
328 g_assert_no_error (error);
329
330 tls = g_tls_server_connection_new (G_IO_STREAM (connection),
331 server_cert, &error);
332 g_assert_no_error (error);
333 g_object_unref (server_cert);
334
335 g_object_set (G_OBJECT (tls),
336 "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED,
337 NULL);
338 g_signal_connect (tls, "accept-certificate",
339 G_CALLBACK (accept_client_certificate), NULL);
340
341 if (g_tls_connection_handshake (G_TLS_CONNECTION (tls), NULL, &error)) {
342 g_output_stream_write_all (g_io_stream_get_output_stream (tls),
343 INTERACTION_TEST_HTTP_RESPONSE,
344 strlen (INTERACTION_TEST_HTTP_RESPONSE),
345 NULL, NULL, &error);
346 g_assert_no_error (error);
347 } else {
348 g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
349 g_clear_error (&error);
350 }
351
352 g_io_stream_close (tls, NULL, &error);
353 g_assert_no_error (error);
354
355 g_object_unref (tls);
356
357 g_main_context_pop_thread_default (thread_context);
358 g_main_context_unref (thread_context);
359 }
360
361 static void
do_tls_interaction_test(void)362 do_tls_interaction_test (void)
363 {
364 GSocketService *service;
365 GSocketAddress *address, *bound_address;
366 SoupSession *session;
367 SoupMessage *msg;
368 GTlsInteraction *interaction;
369 SoupURI *test_uri;
370 GError *error = NULL;
371
372 SOUP_TEST_SKIP_IF_NO_TLS;
373
374 service = g_threaded_socket_service_new (1);
375 address = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
376 g_socket_listener_add_address (G_SOCKET_LISTENER (service), address,
377 G_SOCKET_TYPE_STREAM,
378 G_SOCKET_PROTOCOL_TCP,
379 NULL, &bound_address, &error);
380 g_assert_no_error (error);
381 g_object_unref (address);
382 g_signal_connect (service, "run", G_CALLBACK (got_connection), NULL);
383 g_socket_service_start (service);
384
385 test_uri = soup_uri_new ("https://127.0.0.1");
386 soup_uri_set_port (test_uri, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (bound_address)));
387 g_object_unref (bound_address);
388
389 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
390
391 /* Without a GTlsInteraction */
392 msg = soup_message_new_from_uri ("GET", test_uri);
393 soup_session_send_message (session, msg);
394 soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED);
395 g_object_unref (msg);
396
397 interaction = g_object_new (test_tls_interaction_get_type (), NULL);
398 g_object_set (G_OBJECT (session),
399 SOUP_SESSION_TLS_INTERACTION, interaction,
400 NULL);
401 g_object_unref (interaction);
402
403 /* With a GTlsInteraction */
404 msg = soup_message_new_from_uri ("GET", test_uri);
405 soup_session_send_message (session, msg);
406 soup_test_assert_message_status (msg, SOUP_STATUS_OK);
407 g_assert_true (soup_message_get_https_status (msg, NULL, NULL));
408 g_object_unref (msg);
409
410 soup_uri_free (test_uri);
411 soup_test_session_abort_unref (session);
412
413 g_socket_service_stop (service);
414 g_object_unref (service);
415 }
416
417 static void
server_handler(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * client,gpointer user_data)418 server_handler (SoupServer *server,
419 SoupMessage *msg,
420 const char *path,
421 GHashTable *query,
422 SoupClientContext *client,
423 gpointer user_data)
424 {
425 soup_message_set_status (msg, SOUP_STATUS_OK);
426 soup_message_set_response (msg, "text/plain",
427 SOUP_MEMORY_STATIC,
428 "ok\r\n", 4);
429 }
430
431 int
main(int argc,char ** argv)432 main (int argc, char **argv)
433 {
434 SoupServer *server = NULL;
435 int i, ret;
436 GError *error = NULL;
437
438 test_init (argc, argv, NULL);
439
440 if (tls_available) {
441 server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
442 soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
443 uri = soup_test_server_get_uri (server, "https", "127.0.0.1");
444
445 null_tlsdb = g_tls_file_database_new ("/dev/null", &error);
446 g_assert_no_error (error);
447 } else
448 uri = NULL;
449
450 g_test_add_func ("/ssl/session-properties", do_session_property_tests);
451 g_test_add_func ("/ssl/message-properties/async", do_async_properties_tests);
452 g_test_add_func ("/ssl/message-properties/sync", do_sync_properties_tests);
453 g_test_add_func ("/ssl/tls-interaction", do_tls_interaction_test);
454
455 for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) {
456 g_test_add_data_func (strictness_tests[i].name,
457 &strictness_tests[i],
458 do_strictness_test);
459 }
460
461 ret = g_test_run ();
462
463 if (tls_available) {
464 soup_uri_free (uri);
465 soup_test_server_quit_unref (server);
466 g_object_unref (null_tlsdb);
467 }
468
469 test_cleanup ();
470 return ret;
471 }
472