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