5 * Purple is the legal property of its developers, whose names are too numerous
6 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
25 #include "tls-certificate.h"
29 /* Makes a filename path for a certificate. If id is NULL,
30 * just return the directory
33 make_certificate_path(const gchar
*id
)
35 return g_build_filename(purple_data_dir(),
36 "certificates", "tls",
37 id
!= NULL
? purple_escape_filename(id
) : NULL
,
41 /* Creates the certificate directory if it doesn't exist,
42 * returns TRUE if it's successful or it already exists,
43 * returns FALSE if there was an error.
46 ensure_certificate_dir(GError
**error
)
48 gchar
*dir
= make_certificate_path(NULL
);
51 if (purple_build_dir(dir
, 0700) != 0) {
52 g_set_error_literal(error
, G_FILE_ERROR
,
53 g_file_error_from_errno(errno
),
63 purple_tls_certificate_list_ids()
71 /* Ensure certificate directory exists */
73 if (!ensure_certificate_dir(&error
)) {
74 purple_debug_error("tls-certificate",
75 "Error creating certificate directory: %s",
77 g_clear_error(&error
);
81 /* Open certificate directory */
83 dir_path
= make_certificate_path(NULL
);
84 dir
= g_dir_open(dir_path
, 0, &error
);
87 purple_debug_error("tls-certificate",
88 "Error opening certificate directory (%s): %s",
89 dir_path
, error
->message
);
91 g_clear_error(&error
);
97 /* Traverse the directory listing and create an idlist */
99 while ((entry
= g_dir_read_name(dir
)) != NULL
) {
100 /* Unescape the filename
101 * (GLib owns original string)
103 const char *unescaped
= purple_unescape_filename(entry
);
105 /* Copy the entry name into our list
106 * (Purple own the escaped string)
108 idlist
= g_list_prepend(idlist
, g_strdup(unescaped
));
117 purple_tls_certificate_free_ids(GList
*ids
)
119 g_list_free_full(ids
, g_free
);
123 purple_tls_certificate_new_from_id(const gchar
*id
, GError
**error
)
125 GTlsCertificate
*cert
;
128 g_return_val_if_fail(id
!= NULL
&& id
[0] != '\0', NULL
);
130 /* Load certificate from file if it exists */
132 path
= make_certificate_path(id
);
133 cert
= g_tls_certificate_new_from_file(path
, error
);
140 purple_tls_certificate_trust(const gchar
*id
, GTlsCertificate
*certificate
,
147 g_return_val_if_fail(id
!= NULL
&& id
[0] != '\0', FALSE
);
148 g_return_val_if_fail(G_IS_TLS_CERTIFICATE(certificate
), FALSE
);
150 /* Ensure certificate directory exists */
152 if (!ensure_certificate_dir(error
)) {
156 /* Get the text representation of the certificate */
158 g_object_get(certificate
, "certificate-pem", &pem
, NULL
);
159 g_return_val_if_fail(pem
!= NULL
, FALSE
);
161 /* Save certificate text to a fail */
163 path
= make_certificate_path(id
);
164 ret
= g_file_set_contents(path
, pem
, -1, error
);
172 purple_tls_certificate_distrust(const gchar
*id
, GError
**error
)
177 g_return_val_if_fail(id
!= NULL
&& id
[0] != '\0', FALSE
);
179 /* Delete certificate file if it exists */
181 path
= make_certificate_path(id
);
183 if (g_unlink(path
) != 0) {
184 g_set_error_literal(error
, G_FILE_ERROR
,
185 g_file_error_from_errno(errno
),
195 /* Converts GTlsCertificateFlags to a translated string representation
196 * of the first set error flag in the order checked
199 tls_certificate_flags_to_reason(GTlsCertificateFlags flags
)
201 if (flags
& G_TLS_CERTIFICATE_UNKNOWN_CA
) {
202 return _("The certificate is not trusted because no "
203 "certificate that can verify it is "
204 "currently trusted.");
205 } else if (flags
& G_TLS_CERTIFICATE_BAD_IDENTITY
) {
206 /* Translators: "domain" refers to a DNS domain
207 * (e.g. talk.google.com)
209 return _("The certificate presented is not issued to "
211 } else if (flags
& G_TLS_CERTIFICATE_NOT_ACTIVATED
) {
212 return _("The certificate is not valid yet. Check that your "
213 "computer's date and time are accurate.");
214 } else if (flags
& G_TLS_CERTIFICATE_EXPIRED
) {
215 return _("The certificate has expired and should not be "
216 "considered valid. Check that your "
217 "computer's date and time are accurate.");
218 } else if (flags
& G_TLS_CERTIFICATE_REVOKED
) {
219 return _("The certificate has been revoked.");
220 } else if (flags
& G_TLS_CERTIFICATE_INSECURE
) {
221 return _("The certificate's algorithm is considered insecure.");
223 /* Also catches G_TLS_CERTIFICATE_GENERIC_ERROR here */
224 return _("An unknown certificate error occurred.");
228 /* Holds data for requesting the user to accept a given certificate */
231 GTlsCertificate
*cert
;
232 } UserCertRequestData
;
235 user_cert_request_data_free(UserCertRequestData
*data
)
237 g_return_if_fail(data
!= NULL
);
239 g_free(data
->identity
);
240 g_object_unref(data
->cert
);
246 user_cert_request_accept_cb(UserCertRequestData
*data
)
248 GError
*error
= NULL
;
250 g_return_if_fail(data
!= NULL
);
252 /* User accepted. Trust this certificate */
253 if(!purple_tls_certificate_trust(data
->identity
, data
->cert
, &error
)) {
254 purple_debug_error("tls-certificate",
255 "Error trusting certificate '%s': %s",
256 data
->identity
, error
->message
);
257 g_clear_error(&error
);
260 user_cert_request_data_free(data
);
264 user_cert_request_deny_cb(UserCertRequestData
*data
)
266 /* User denied. Free data related to the requst */
267 user_cert_request_data_free(data
);
270 /* Prompts the user to accept the certificate as it failed due to the
274 request_accept_certificate(const gchar
*identity
, GTlsCertificate
*peer_cert
,
275 GTlsCertificateFlags errors
)
277 UserCertRequestData
*data
;
280 g_return_if_fail(identity
!= NULL
&& identity
[0] != '\0');
281 g_return_if_fail(G_IS_TLS_CERTIFICATE(peer_cert
));
282 g_return_if_fail(errors
!= 0);
284 data
= g_new(UserCertRequestData
, 1);
285 data
->identity
= g_strdup(identity
);
286 data
->cert
= g_object_ref(peer_cert
);
288 primary
= g_strdup_printf(_("Accept certificate for %s?"), identity
);
289 purple_request_certificate(data
,
290 _("TLS Certificate Verification"),
292 tls_certificate_flags_to_reason(errors
),
294 _("Accept"), G_CALLBACK(user_cert_request_accept_cb
),
295 _("Reject"), G_CALLBACK(user_cert_request_deny_cb
),
300 /* Called when a GTlsConnection (which this handler has been connected to)
301 * has an error validating its certificate.
302 * Returns TRUE if the certificate is already trusted, so the connection
304 * Returns FALSE if the certificate is not trusted, causing the
305 * connection's handshake to fail, and then prompts the user to accept
309 accept_certificate_cb(GTlsConnection
*conn
, GTlsCertificate
*peer_cert
,
310 GTlsCertificateFlags errors
, gpointer user_data
)
312 GTlsCertificate
*trusted_cert
;
313 GSocketConnectable
*connectable
;
314 const gchar
*identity
;
316 g_return_val_if_fail(G_IS_TLS_CLIENT_CONNECTION(conn
), FALSE
);
317 g_return_val_if_fail(G_IS_TLS_CERTIFICATE(peer_cert
), FALSE
);
319 /* Get the certificate identity from the GTlsClientConnection */
321 connectable
= g_tls_client_connection_get_server_identity(
322 G_TLS_CLIENT_CONNECTION(conn
));
324 g_return_val_if_fail(G_IS_SOCKET_CONNECTABLE(connectable
), FALSE
);
326 /* identity is owned by the connectable */
327 if (G_IS_NETWORK_ADDRESS(connectable
)) {
328 identity
= g_network_address_get_hostname(
329 G_NETWORK_ADDRESS(connectable
));
330 } else if (G_IS_NETWORK_SERVICE(connectable
)) {
331 identity
= g_network_service_get_domain(
332 G_NETWORK_SERVICE(connectable
));
334 g_return_val_if_reached(FALSE
);
337 /* See if a trusted certificate matching the peer certificate exists */
339 trusted_cert
= purple_tls_certificate_new_from_id(identity
, NULL
);
341 if (trusted_cert
!= NULL
&&
342 g_tls_certificate_is_same(peer_cert
, trusted_cert
)) {
343 /* It's manually trusted. Accept certificate */
344 g_object_unref(trusted_cert
);
348 g_clear_object(&trusted_cert
);
350 /* Certificate failed and isn't trusted.
351 * Fail certificate and prompt user.
354 request_accept_certificate(identity
, peer_cert
, errors
);
360 purple_tls_certificate_attach_to_tls_connection(GTlsConnection
*conn
)
362 return g_object_connect(conn
, "signal::accept-certificate",
363 accept_certificate_cb
, NULL
, NULL
);
366 /* Called when GSocketClient signals an event.
367 * Calls purple_tls_certificate_attach_to_tls_connection() on the client's
368 * connection when it's about to handshake.
371 socket_client_event_cb(GSocketClient
*client
, GSocketClientEvent event
,
372 GSocketConnectable
*connectable
, GIOStream
*connection
,
375 if (event
== G_SOCKET_CLIENT_TLS_HANDSHAKING
) {
376 /* Attach libpurple's certificate subsystem to the
377 * GTlsConnection right before it starts the handshake
379 purple_tls_certificate_attach_to_tls_connection(
380 G_TLS_CONNECTION(connection
));
385 purple_tls_certificate_attach_to_socket_client(GSocketClient
*client
)
387 return g_object_connect(client
, "signal::event",
388 socket_client_event_cb
, NULL
, NULL
);