• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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