1 /*
2  * e-soup-ssl-trust.c
3  *
4  * This library is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library. If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 /**
19  * SECTION: e-soup-ssl-trust
20  * @include: libedataserver/libedataserver.h
21  * @short_description: SSL certificate trust handling for WebDAV sources
22  *
23  *
24  **/
25 
26 #include "evolution-data-server-config.h"
27 
28 #include "e-source-authentication.h"
29 #include "e-source-webdav.h"
30 
31 #include "e-soup-ssl-trust.h"
32 
33 typedef struct _ESoupSslTrustData {
34 	SoupMessage *soup_message; /* weak */
35 	ESource *source;
36 
37 	GClosure *accept_certificate_closure;
38 } ESoupSslTrustData;
39 
40 static gboolean
e_soup_ssl_trust_accept_certificate_cb(GTlsConnection * conn,GTlsCertificate * peer_cert,GTlsCertificateFlags errors,gpointer user_data)41 e_soup_ssl_trust_accept_certificate_cb (GTlsConnection *conn,
42 					GTlsCertificate *peer_cert,
43 					GTlsCertificateFlags errors,
44 					gpointer user_data)
45 {
46 	ESoupSslTrustData *handler = user_data;
47 	ETrustPromptResponse response;
48 	SoupURI *soup_uri;
49 	const gchar *host;
50 	gchar *auth_host = NULL;
51 
52 	soup_uri = soup_message_get_uri (handler->soup_message);
53 	if (!soup_uri || !soup_uri_get_host (soup_uri))
54 		return FALSE;
55 
56 	host = soup_uri_get_host (soup_uri);
57 
58 	if (e_source_has_extension (handler->source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
59 		ESourceAuthentication *extension_authentication;
60 
61 		extension_authentication = e_source_get_extension (handler->source, E_SOURCE_EXTENSION_AUTHENTICATION);
62 		auth_host = e_source_authentication_dup_host (extension_authentication);
63 
64 		if (auth_host && *auth_host) {
65 			/* Use the 'host' from the Authentication extension, because
66 			   it's the one used when storing the trust prompt result.
67 			   The SoupMessage can be redirected, thus it would not ever match. */
68 			host = auth_host;
69 		} else {
70 			g_free (auth_host);
71 			auth_host = NULL;
72 		}
73 	}
74 
75 	response = e_source_webdav_verify_ssl_trust (
76 		e_source_get_extension (handler->source, E_SOURCE_EXTENSION_WEBDAV_BACKEND),
77 		host, peer_cert, errors);
78 
79 	g_free (auth_host);
80 
81 	return (response == E_TRUST_PROMPT_RESPONSE_ACCEPT ||
82 	        response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY);
83 }
84 
85 static void
e_soup_ssl_trust_network_event_cb(SoupMessage * msg,GSocketClientEvent event,GIOStream * connection,gpointer user_data)86 e_soup_ssl_trust_network_event_cb (SoupMessage *msg,
87 				   GSocketClientEvent event,
88 				   GIOStream *connection,
89 				   gpointer user_data)
90 {
91 	ESoupSslTrustData *handler = user_data;
92 
93 	/* It's either a GTlsConnection or a GTcpConnection */
94 	if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING &&
95 	    G_IS_TLS_CONNECTION (connection)) {
96 		g_signal_connect_closure (
97 			G_TLS_CONNECTION (connection), "accept-certificate",
98 			handler->accept_certificate_closure, FALSE);
99 	}
100 }
101 
102 static void
e_soup_ssl_trust_message_finalized_cb(gpointer data,GObject * unused_message)103 e_soup_ssl_trust_message_finalized_cb (gpointer data,
104 				       GObject *unused_message)
105 {
106 	ESoupSslTrustData *handler;
107 
108 	/* The network event handler will be disconnected from the message just
109 	 * before this is called. */
110 	handler = data;
111 
112 	g_clear_object (&handler->source);
113 
114 	/* Synchronously disconnects the accept certificate handler from all
115 	 * GTlsConnections. */
116 	g_closure_invalidate (handler->accept_certificate_closure);
117 	g_closure_unref (handler->accept_certificate_closure);
118 
119 	g_free (handler);
120 }
121 
122 /**
123  * e_soup_ssl_trust_connect:
124  * @soup_message: a #SoupMessage about to be sent to the source
125  * @source: an #ESource that uses WebDAV
126  *
127  * Sets up automatic SSL certificate trust handling for @soup_message using the trust
128  * data stored in @source's WebDAV extension. If @soup_message is about to be sent on
129  * an SSL connection with an invalid certificate, the code checks if the WebDAV
130  * extension already has a trust response for that certificate and verifies it
131  * with e_source_webdav_verify_ssl_trust(). If the verification fails, then
132  * the @soup_message send also fails.
133  *
134  * This works by connecting to the "network-event" signal on @soup_message and
135  * connecting to the "accept-certificate" signal on each #GTlsConnection for
136  * which @soup_message reports a #G_SOCKET_CLIENT_TLS_HANDSHAKING event. These
137  * handlers are torn down automatically when @soup_message is disposed. This process
138  * is not thread-safe; it is sufficient for safety if all use of @soup_message's
139  * session and the disposal of @soup_message occur in the same thread.
140  *
141  * Since: 3.16
142  **/
143 void
e_soup_ssl_trust_connect(SoupMessage * soup_message,ESource * source)144 e_soup_ssl_trust_connect (SoupMessage *soup_message,
145                           ESource *source)
146 {
147 	ESoupSslTrustData *handler;
148 
149 	g_return_if_fail (SOUP_IS_MESSAGE (soup_message));
150 	g_return_if_fail (E_IS_SOURCE (source));
151 
152 	handler = g_malloc (sizeof (ESoupSslTrustData));
153 	handler->soup_message = soup_message;
154 	g_object_weak_ref (G_OBJECT (soup_message), e_soup_ssl_trust_message_finalized_cb, handler);
155 	handler->source = g_object_ref (source);
156 	handler->accept_certificate_closure = g_cclosure_new (G_CALLBACK (e_soup_ssl_trust_accept_certificate_cb), handler, NULL);
157 
158 	g_closure_ref (handler->accept_certificate_closure);
159 	g_closure_sink (handler->accept_certificate_closure);
160 
161 	g_signal_connect (
162 		soup_message, "network-event",
163 		G_CALLBACK (e_soup_ssl_trust_network_event_cb), handler);
164 }
165