/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ #include "test-utils.h" SoupURI *uri; GTlsDatabase *null_tlsdb; static void do_properties_test_for_session (SoupSession *session) { SoupMessage *msg; GTlsCertificate *cert; GTlsCertificateFlags flags; msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_OK); if (soup_message_get_https_status (msg, &cert, &flags)) { g_assert_true (G_IS_TLS_CERTIFICATE (cert)); g_assert_cmpuint (flags, ==, G_TLS_CERTIFICATE_UNKNOWN_CA); } else soup_test_assert (FALSE, "Response not https"); g_test_bug ("665182"); g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); g_object_unref (msg); } static void do_async_properties_tests (void) { SoupSession *session; SOUP_TEST_SKIP_IF_NO_TLS; session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_object_set (G_OBJECT (session), SOUP_SESSION_TLS_DATABASE, null_tlsdb, SOUP_SESSION_SSL_STRICT, FALSE, NULL); do_properties_test_for_session (session); soup_test_session_abort_unref (session); } static void do_sync_properties_tests (void) { SoupSession *session; SOUP_TEST_SKIP_IF_NO_TLS; session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); g_object_set (G_OBJECT (session), SOUP_SESSION_TLS_DATABASE, null_tlsdb, SOUP_SESSION_SSL_STRICT, FALSE, NULL); do_properties_test_for_session (session); soup_test_session_abort_unref (session); } typedef struct { const char *name; gboolean sync; gboolean strict; gboolean with_ca_list; guint expected_status; } StrictnessTest; static const StrictnessTest strictness_tests[] = { { "/ssl/strictness/async/strict/with-ca", FALSE, TRUE, TRUE, SOUP_STATUS_OK }, { "/ssl/strictness/async/strict/without-ca", FALSE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED }, { "/ssl/strictness/async/non-strict/with-ca", FALSE, FALSE, TRUE, SOUP_STATUS_OK }, { "/ssl/strictness/async/non-strict/without-ca", FALSE, FALSE, FALSE, SOUP_STATUS_OK }, { "/ssl/strictness/sync/strict/with-ca", TRUE, TRUE, TRUE, SOUP_STATUS_OK }, { "/ssl/strictness/sync/strict/without-ca", TRUE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED }, { "/ssl/strictness/sync/non-strict/with-ca", TRUE, FALSE, TRUE, SOUP_STATUS_OK }, { "/ssl/strictness/sync/non-strict/without-ca", TRUE, FALSE, FALSE, SOUP_STATUS_OK }, }; static void do_strictness_test (gconstpointer data) { const StrictnessTest *test = data; SoupSession *session; SoupMessage *msg; GTlsCertificateFlags flags = 0; SOUP_TEST_SKIP_IF_NO_TLS; session = soup_test_session_new (test->sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC, NULL); if (!test->strict) { g_object_set (G_OBJECT (session), SOUP_SESSION_SSL_STRICT, FALSE, NULL); } if (!test->with_ca_list) { g_object_set (G_OBJECT (session), SOUP_SESSION_TLS_DATABASE, null_tlsdb, NULL); } msg = soup_message_new_from_uri ("GET", uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, test->expected_status); g_test_bug ("690176"); g_assert_true (soup_message_get_https_status (msg, NULL, &flags)); g_test_bug ("665182"); if (test->with_ca_list && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) g_assert_true (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); else g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED); if (msg->status_code == SOUP_STATUS_SSL_FAILED && test->expected_status != SOUP_STATUS_SSL_FAILED) debug_printf (1, " tls error flags: 0x%x\n", flags); g_object_unref (msg); soup_test_session_abort_unref (session); } static void property_changed (GObject *object, GParamSpec *param, gpointer user_data) { gboolean *changed = user_data; *changed = TRUE; } static void do_session_property_tests (void) { gboolean use_system_changed, tlsdb_changed, ca_file_changed; gboolean use_system; GTlsDatabase *tlsdb; char *ca_file; SoupSession *session; GParamSpec *pspec; g_test_bug ("673678"); SOUP_TEST_SKIP_IF_NO_TLS; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; session = soup_session_async_new (); G_GNUC_END_IGNORE_DEPRECATIONS; /* Temporarily undeprecate SOUP_SESSION_SSL_CA_FILE to avoid warnings. */ pspec = g_object_class_find_property (g_type_class_peek (SOUP_TYPE_SESSION), SOUP_SESSION_SSL_CA_FILE); pspec->flags &= ~G_PARAM_DEPRECATED; g_signal_connect (session, "notify::ssl-use-system-ca-file", G_CALLBACK (property_changed), &use_system_changed); g_signal_connect (session, "notify::tls-database", G_CALLBACK (property_changed), &tlsdb_changed); g_signal_connect (session, "notify::ssl-ca-file", G_CALLBACK (property_changed), &ca_file_changed); g_object_get (G_OBJECT (session), "ssl-use-system-ca-file", &use_system, "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); soup_test_assert (!use_system, "ssl-use-system-ca-file defaults to TRUE"); soup_test_assert (tlsdb == NULL, "tls-database set by default"); soup_test_assert (ca_file == NULL, "ca-file set by default"); use_system_changed = tlsdb_changed = ca_file_changed = FALSE; g_object_set (G_OBJECT (session), "ssl-use-system-ca-file", TRUE, NULL); g_object_get (G_OBJECT (session), "ssl-use-system-ca-file", &use_system, "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); soup_test_assert (use_system, "setting ssl-use-system-ca-file failed"); g_assert_true (use_system_changed); soup_test_assert (tlsdb != NULL, "setting ssl-use-system-ca-file didn't set tls-database"); g_assert_true (tlsdb_changed); g_clear_object (&tlsdb); soup_test_assert (ca_file == NULL, "setting ssl-use-system-ca-file set ssl-ca-file"); g_assert_false (ca_file_changed); use_system_changed = tlsdb_changed = ca_file_changed = FALSE; g_object_set (G_OBJECT (session), "ssl-ca-file", g_test_get_filename (G_TEST_DIST, "/test-cert.pem", NULL), NULL); g_object_get (G_OBJECT (session), "ssl-use-system-ca-file", &use_system, "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); soup_test_assert (!use_system, "setting ssl-ca-file left ssl-use-system-ca-file set"); g_assert_true (use_system_changed); soup_test_assert (tlsdb != NULL, "setting ssl-ca-file didn't set tls-database"); g_assert_true (tlsdb_changed); g_clear_object (&tlsdb); soup_test_assert (ca_file != NULL, "setting ssl-ca-file failed"); g_assert_true (ca_file_changed); g_free (ca_file); use_system_changed = tlsdb_changed = ca_file_changed = FALSE; g_object_set (G_OBJECT (session), "tls-database", NULL, NULL); g_object_get (G_OBJECT (session), "ssl-use-system-ca-file", &use_system, "tls-database", &tlsdb, "ssl-ca-file", &ca_file, NULL); soup_test_assert (!use_system, "setting tls-database NULL left ssl-use-system-ca-file set"); g_assert_false (use_system_changed); soup_test_assert (tlsdb == NULL, "setting tls-database NULL failed"); g_assert_true (tlsdb_changed); soup_test_assert (ca_file == NULL, "setting tls-database didn't clear ssl-ca-file"); g_assert_true (ca_file_changed); soup_test_session_abort_unref (session); /* Re-deprecate SOUP_SESSION_SSL_CA_FILE */ pspec->flags |= G_PARAM_DEPRECATED; } /* GTlsInteraction subclass for do_interaction_test */ typedef GTlsInteraction TestTlsInteraction; typedef GTlsInteractionClass TestTlsInteractionClass; GType test_tls_interaction_get_type (void); G_DEFINE_TYPE (TestTlsInteraction, test_tls_interaction, G_TYPE_TLS_INTERACTION); static void test_tls_interaction_init (TestTlsInteraction *interaction) { } static GTlsInteractionResult test_tls_interaction_request_certificate (GTlsInteraction *interaction, GTlsConnection *connection, GTlsCertificateRequestFlags flags, GCancellable *cancellable, GError **error) { GTlsCertificate *cert; const char *ssl_cert_file, *ssl_key_file; GError *my_error = NULL; /* Yes, we use the same certificate for the client as for the server. Shrug */ ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL); ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL); cert = g_tls_certificate_new_from_files (ssl_cert_file, ssl_key_file, &my_error); g_assert_no_error (my_error); g_tls_connection_set_certificate (connection, cert); g_object_unref (cert); return G_TLS_INTERACTION_HANDLED; } static void test_tls_interaction_class_init (TestTlsInteractionClass *klass) { GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass); interaction_class->request_certificate = test_tls_interaction_request_certificate; } #define INTERACTION_TEST_HTTP_RESPONSE "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nOK\r\n" static gboolean accept_client_certificate (GTlsConnection *server, GTlsCertificate *client_cert, GTlsCertificateFlags errors) { return TRUE; } static void got_connection (GThreadedSocketService *service, GSocketConnection *connection, GObject *source_object) { GIOStream *tls; GTlsCertificate *server_cert; GError *error = NULL; const char *ssl_cert_file, *ssl_key_file; GMainContext *thread_context; thread_context = g_main_context_new (); g_main_context_push_thread_default (thread_context); ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL); ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL); server_cert = g_tls_certificate_new_from_files (ssl_cert_file, ssl_key_file, &error); g_assert_no_error (error); tls = g_tls_server_connection_new (G_IO_STREAM (connection), server_cert, &error); g_assert_no_error (error); g_object_unref (server_cert); g_object_set (G_OBJECT (tls), "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED, NULL); g_signal_connect (tls, "accept-certificate", G_CALLBACK (accept_client_certificate), NULL); if (g_tls_connection_handshake (G_TLS_CONNECTION (tls), NULL, &error)) { g_output_stream_write_all (g_io_stream_get_output_stream (tls), INTERACTION_TEST_HTTP_RESPONSE, strlen (INTERACTION_TEST_HTTP_RESPONSE), NULL, NULL, &error); g_assert_no_error (error); } else { g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED); g_clear_error (&error); } g_io_stream_close (tls, NULL, &error); g_assert_no_error (error); g_object_unref (tls); g_main_context_pop_thread_default (thread_context); g_main_context_unref (thread_context); } static void do_tls_interaction_test (void) { GSocketService *service; GSocketAddress *address, *bound_address; SoupSession *session; SoupMessage *msg; GTlsInteraction *interaction; SoupURI *test_uri; GError *error = NULL; SOUP_TEST_SKIP_IF_NO_TLS; service = g_threaded_socket_service_new (1); address = g_inet_socket_address_new_from_string ("127.0.0.1", 0); g_socket_listener_add_address (G_SOCKET_LISTENER (service), address, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, NULL, &bound_address, &error); g_assert_no_error (error); g_object_unref (address); g_signal_connect (service, "run", G_CALLBACK (got_connection), NULL); g_socket_service_start (service); test_uri = soup_uri_new ("https://127.0.0.1"); soup_uri_set_port (test_uri, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (bound_address))); g_object_unref (bound_address); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); /* Without a GTlsInteraction */ msg = soup_message_new_from_uri ("GET", test_uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED); g_object_unref (msg); interaction = g_object_new (test_tls_interaction_get_type (), NULL); g_object_set (G_OBJECT (session), SOUP_SESSION_TLS_INTERACTION, interaction, NULL); g_object_unref (interaction); /* With a GTlsInteraction */ msg = soup_message_new_from_uri ("GET", test_uri); soup_session_send_message (session, msg); soup_test_assert_message_status (msg, SOUP_STATUS_OK); g_assert_true (soup_message_get_https_status (msg, NULL, NULL)); g_object_unref (msg); soup_uri_free (test_uri); soup_test_session_abort_unref (session); g_socket_service_stop (service); g_object_unref (service); } static void server_handler (SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { soup_message_set_status (msg, SOUP_STATUS_OK); soup_message_set_response (msg, "text/plain", SOUP_MEMORY_STATIC, "ok\r\n", 4); } int main (int argc, char **argv) { SoupServer *server = NULL; int i, ret; GError *error = NULL; test_init (argc, argv, NULL); if (tls_available) { server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_handler, NULL, NULL); uri = soup_test_server_get_uri (server, "https", "127.0.0.1"); null_tlsdb = g_tls_file_database_new ("/dev/null", &error); g_assert_no_error (error); } else uri = NULL; g_test_add_func ("/ssl/session-properties", do_session_property_tests); g_test_add_func ("/ssl/message-properties/async", do_async_properties_tests); g_test_add_func ("/ssl/message-properties/sync", do_sync_properties_tests); g_test_add_func ("/ssl/tls-interaction", do_tls_interaction_test); for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) { g_test_add_data_func (strictness_tests[i].name, &strictness_tests[i], do_strictness_test); } ret = g_test_run (); if (tls_available) { soup_uri_free (uri); soup_test_server_quit_unref (server); g_object_unref (null_tlsdb); } test_cleanup (); return ret; }