• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <glib.h>
2 #include <glib/gstdio.h>
3 
4 #include <stdio.h>
5 #include "test-utils.h"
6 
7 #define DB_FILE "hsts-db.sqlite"
8 
9 SoupURI *http_uri;
10 SoupURI *https_uri;
11 
12 /* This server pseudo-implements the HSTS spec in order to allow us to
13    test the Soup HSTS feature.
14  */
15 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)16 server_callback  (SoupServer *server, SoupMessage *msg,
17 		  const char *path, GHashTable *query,
18 		  SoupClientContext *context, gpointer data)
19 {
20 	const char *server_protocol = data;
21 
22 	if (strcmp (server_protocol, "http") == 0) {
23 		char *uri_string;
24 		SoupURI *uri = soup_uri_new ("https://localhost");
25 		soup_uri_set_path (uri, path);
26 		uri_string = soup_uri_to_string (uri, FALSE);
27 		fprintf (stderr, "server is redirecting to HTTPS\n");
28 		soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, uri_string);
29 		soup_uri_free (uri);
30 		g_free (uri_string);
31 	} else if (strcmp (server_protocol, "https") == 0) {
32 		soup_message_set_status (msg, SOUP_STATUS_OK);
33 		if (strcmp (path, "/long-lasting") == 0) {
34 			soup_message_headers_append (msg->response_headers,
35 						     "Strict-Transport-Security",
36 						     "max-age=31536000");
37 		} else if (strcmp (path, "/two-seconds") == 0) {
38 			soup_message_headers_append (msg->response_headers,
39 						     "Strict-Transport-Security",
40 						     "max-age=2");
41 		} else if (strcmp (path, "/delete") == 0) {
42 			soup_message_headers_append (msg->response_headers,
43 						     "Strict-Transport-Security",
44 						     "max-age=0");
45 		} else if (strcmp (path, "/subdomains") == 0) {
46 			soup_message_headers_append (msg->response_headers,
47 						     "Strict-Transport-Security",
48 						     "max-age=31536000; includeSubDomains");
49 		}
50                 else if (strcmp (path, "/very-long-lasting") == 0) {
51 			soup_message_headers_append (msg->response_headers,
52 						     "Strict-Transport-Security",
53 						     "max-age=631138519");
54 		}
55 	}
56 }
57 
58 static void
session_get_uri(SoupSession * session,const char * uri,SoupStatus expected_status)59 session_get_uri (SoupSession *session, const char *uri, SoupStatus expected_status)
60 {
61 	SoupMessage *msg;
62 
63 	msg = soup_message_new ("GET", uri);
64 	soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
65 	soup_session_send_message (session, msg);
66 	soup_test_assert_message_status (msg, expected_status);
67 	g_object_unref (msg);
68 }
69 
70 /* The HSTS specification does not handle custom ports, so we need to
71  * rewrite the URI in the request and add the port where the server is
72  * listening before it is sent, to be able to connect to the localhost
73  * port where the server is actually running.
74  */
75 static void
rewrite_message_uri(SoupMessage * msg)76 rewrite_message_uri (SoupMessage *msg)
77 {
78 	if (soup_uri_get_scheme (soup_message_get_uri (msg)) == SOUP_URI_SCHEME_HTTP)
79 		soup_uri_set_port (soup_message_get_uri (msg), soup_uri_get_port (http_uri));
80 	else if (soup_uri_get_scheme (soup_message_get_uri (msg)) == SOUP_URI_SCHEME_HTTPS)
81 		soup_uri_set_port (soup_message_get_uri (msg), soup_uri_get_port (https_uri));
82 	else
83 		g_assert_not_reached();
84 }
85 
86 static void
on_message_restarted(SoupMessage * msg,gpointer data)87 on_message_restarted (SoupMessage *msg,
88 		     gpointer data)
89 {
90 	rewrite_message_uri (msg);
91 }
92 
93 static void
on_request_queued(SoupSession * session,SoupMessage * msg,gpointer data)94 on_request_queued (SoupSession *session,
95 		   SoupMessage *msg,
96 		   gpointer data)
97 {
98 	g_signal_connect (msg, "restarted", G_CALLBACK (on_message_restarted), NULL);
99 
100 	rewrite_message_uri (msg);
101 }
102 
103 static SoupSession *
hsts_db_session_new(void)104 hsts_db_session_new (void)
105 {
106 	SoupHSTSEnforcer *hsts_db = soup_hsts_enforcer_db_new (DB_FILE);
107 
108 	SoupSession *session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
109 						      SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
110 						      SOUP_SESSION_ADD_FEATURE, hsts_db,
111 						      NULL);
112 	g_signal_connect (session, "request-queued", G_CALLBACK (on_request_queued), NULL);
113 	g_object_unref (hsts_db);
114 
115 	return session;
116 }
117 
118 
119 static void
do_hsts_db_persistency_test(void)120 do_hsts_db_persistency_test (void)
121 {
122 	SoupSession *session = hsts_db_session_new ();
123 	session_get_uri (session, "https://localhost/long-lasting", SOUP_STATUS_OK);
124 	session_get_uri (session, "http://localhost", SOUP_STATUS_OK);
125 	soup_test_session_abort_unref (session);
126 
127 	session = hsts_db_session_new ();
128 	session_get_uri (session, "http://localhost", SOUP_STATUS_OK);
129 	soup_test_session_abort_unref (session);
130 
131 	g_remove (DB_FILE);
132 }
133 
134 static void
do_hsts_db_subdomains_test(void)135 do_hsts_db_subdomains_test (void)
136 {
137 	SoupSession *session = hsts_db_session_new ();
138 	session_get_uri (session, "https://localhost/subdomains", SOUP_STATUS_OK);
139 	soup_test_session_abort_unref (session);
140 
141 	session = hsts_db_session_new ();
142 	session_get_uri (session, "http://subdomain.localhost", SOUP_STATUS_SSL_FAILED);
143 	soup_test_session_abort_unref (session);
144 
145 	g_remove (DB_FILE);
146 }
147 
148 static void
do_hsts_db_large_max_age_test(void)149 do_hsts_db_large_max_age_test (void)
150 {
151 	SoupSession *session = hsts_db_session_new ();
152 	session_get_uri (session, "https://localhost/very-long-lasting", SOUP_STATUS_OK);
153 	session_get_uri (session, "http://localhost", SOUP_STATUS_OK);
154 	soup_test_session_abort_unref (session);
155 
156 	session = hsts_db_session_new ();
157 	session_get_uri (session, "http://localhost", SOUP_STATUS_OK);
158 	soup_test_session_abort_unref (session);
159 
160 	g_remove (DB_FILE);
161 }
162 
163 int
main(int argc,char ** argv)164 main (int argc, char **argv)
165 {
166 	int ret;
167 	SoupServer *server;
168 	SoupServer *https_server = NULL;
169 
170 	test_init (argc, argv, NULL);
171 
172 	server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
173 	soup_server_add_handler (server, NULL, server_callback, "http", NULL);
174 	http_uri = soup_test_server_get_uri (server, "http", NULL);
175 
176 	if (tls_available) {
177 		https_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
178 		soup_server_add_handler (https_server, NULL, server_callback, "https", NULL);
179 		https_uri = soup_test_server_get_uri (https_server, "https", NULL);
180 	}
181 
182 	g_test_add_func ("/hsts-db/basic", do_hsts_db_persistency_test);
183 	g_test_add_func ("/hsts-db/subdomains", do_hsts_db_subdomains_test);
184 	g_test_add_func ("/hsts-db/large-max-age", do_hsts_db_large_max_age_test);
185 
186 	ret = g_test_run ();
187 
188 	soup_uri_free (http_uri);
189 	soup_test_server_quit_unref (server);
190 
191 	if (tls_available) {
192 		soup_uri_free (https_uri);
193 		soup_test_server_quit_unref (https_server);
194 	}
195 
196 	test_cleanup ();
197 	return ret;
198 }
199