[gvfs] dav: Verify TLS certificates
- From: Ross Lagerwall <rossl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gvfs] dav: Verify TLS certificates
- Date: Thu, 9 Apr 2015 21:32:34 +0000 (UTC)
commit f5ee590e2e14d9419fb7c30d05d9be7d0aee3532
Author: Ross Lagerwall <rosslagerwall gmail com>
Date: Sat Feb 28 18:41:10 2015 +0000
dav: Verify TLS certificates
When mounting a secure webdav share, verify the certificate using the
system certificate store, or if that is not possible, ask the user
whether it is OK.
ssl-strict is enabled for the SoupSession. If the first connection
attempt fails, the certificate is presented to the user. If they agree
to the certificate, then it is stored and compared with the certificate
presented on each subsequent connection. It is an error if the
certificate changes.
https://bugzilla.gnome.org/show_bug.cgi?id=708306
daemon/gvfsbackenddav.c | 103 ++++++++++++++++++++++++++++++++++++++++------
1 files changed, 89 insertions(+), 14 deletions(-)
---
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c
index 2d2704a..eae7511 100644
--- a/daemon/gvfsbackenddav.c
+++ b/daemon/gvfsbackenddav.c
@@ -58,6 +58,7 @@
#include "gvfsjobenumerate.h"
#include "gvfsjobpush.h"
#include "gvfsdaemonprotocol.h"
+#include "gvfsdaemonutils.h"
#ifdef HAVE_AVAHI
#include "gvfsdnssdutils.h"
@@ -100,6 +101,10 @@ struct _GVfsBackendDav
MountAuthData auth_info;
+ /* Used for user-verified secure connections. */
+ GTlsCertificate *certificate;
+ GTlsCertificateFlags certificate_errors;
+
#ifdef HAVE_AVAHI
/* only set if we're handling a [dav|davs]+sd:// mounts */
GVfsDnsSdResolver *resolver;
@@ -124,7 +129,9 @@ g_vfs_backend_dav_finalize (GObject *object)
#endif
mount_auth_info_free (&(dav_backend->auth_info));
-
+
+ g_clear_object (&dav_backend->certificate);
+
if (G_OBJECT_CLASS (g_vfs_backend_dav_parent_class)->finalize)
(*G_OBJECT_CLASS (g_vfs_backend_dav_parent_class)->finalize) (object);
}
@@ -500,6 +507,29 @@ g_vfs_backend_dav_setup_display_name (GVfsBackend *backend)
g_free (display_name);
}
+static void
+certificate_error_handler (SoupMessage *msg,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GVfsBackendDav *dav = G_VFS_BACKEND_DAV (user_data);
+ GTlsCertificate *certificate;
+ GTlsCertificateFlags errors;
+
+ /* Fail the message if the certificate errors change or the certificate is
+ * different. */
+ if (soup_message_get_https_status (msg, &certificate, &errors))
+ {
+ if (errors != dav->certificate_errors ||
+ !g_tls_certificate_is_same (certificate, dav->certificate))
+ {
+ soup_session_cancel_message (G_VFS_BACKEND_HTTP (dav)->session,
+ msg,
+ SOUP_STATUS_SSL_FAILED);
+ }
+ }
+}
+
static guint
g_vfs_backend_dav_send_message (GVfsBackend *backend, SoupMessage *message)
{
@@ -512,12 +542,30 @@ g_vfs_backend_dav_send_message (GVfsBackend *backend, SoupMessage *message)
/* We have our own custom redirect handler */
soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT);
+ if (G_VFS_BACKEND_DAV (backend)->certificate_errors)
+ g_signal_connect (message, "notify::tls-errors",
+ G_CALLBACK (certificate_error_handler), backend);
+
soup_message_add_header_handler (message, "got_body", "Location",
G_CALLBACK (redirect_handler), session);
return http_backend_send_message (backend, message);
}
+static void
+g_vfs_backend_dav_queue_message (GVfsBackend *backend,
+ SoupMessage *msg,
+ SoupSessionCallback callback,
+ gpointer user_data)
+{
+ if (G_VFS_BACKEND_DAV (backend)->certificate_errors)
+ g_signal_connect (msg, "notify::tls-errors",
+ G_CALLBACK (certificate_error_handler), backend);
+
+ soup_session_queue_message (G_VFS_BACKEND_HTTP (backend)->session, msg,
+ callback, user_data);
+}
+
/* ************************************************************************* */
/* generic xml parsing functions */
@@ -1832,6 +1880,9 @@ do_mount (GVfsBackend *backend,
session = G_VFS_BACKEND_HTTP (backend)->session;
G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
+ /* Override the HTTP backend's default. */
+ g_object_set (session, "ssl-strict", TRUE, NULL);
+
data = &(G_VFS_BACKEND_DAV (backend)->auth_info);
data->mount_source = g_object_ref (mount_source);
data->server_auth.username = g_strdup (mount_base->user);
@@ -1853,8 +1904,34 @@ do_mount (GVfsBackend *backend,
SoupURI *cur_uri;
status = g_vfs_backend_dav_send_message (backend, msg_opts);
-
is_success = SOUP_STATUS_IS_SUCCESSFUL (status);
+
+ /* If SSL is used and the certificate verifies OK, then ssl-strict remains
+ * on for all further connections.
+ * If SSL is used and the certificate does not verify OK, then the user
+ * gets a chance to override it. If they do, ssl-strict is disabled but
+ * the certificate is stored, and checked on each subsequent connection to
+ * ensure that it hasn't changed. */
+ if (status == SOUP_STATUS_SSL_FAILED &&
+ !dav_backend->certificate_errors)
+ {
+ GTlsCertificate *certificate;
+ GTlsCertificateFlags errors;
+
+ soup_message_get_https_status (msg_opts, &certificate, &errors);
+
+ if (gvfs_accept_certificate (mount_source, certificate, errors))
+ {
+ g_object_set (session, "ssl-strict", FALSE, NULL);
+ dav_backend->certificate = g_object_ref (certificate);
+ dav_backend->certificate_errors = errors;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
is_webdav = is_success && sm_has_header (msg_opts, "DAV");
soup_message_headers_clear (msg_opts->response_headers);
@@ -2271,7 +2348,7 @@ try_open_for_read (GVfsBackend *backend,
}
g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
- http_backend_queue_message (backend, msg, try_open_stat_done, job);
+ g_vfs_backend_dav_queue_message (backend, msg, try_open_stat_done, job);
return TRUE;
}
@@ -2335,7 +2412,7 @@ try_create (GVfsBackend *backend,
g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
- http_backend_queue_message (backend, msg, try_create_tested_existence, job);
+ g_vfs_backend_dav_queue_message (backend, msg, try_create_tested_existence, job);
return TRUE;
}
@@ -2423,8 +2500,8 @@ try_replace (GVfsBackend *backend,
soup_message_headers_append (msg->request_headers, "If-Match", etag);
g_vfs_job_set_backend_data (G_VFS_JOB (job), op_backend, NULL);
- soup_session_queue_message (op_backend->session, msg,
- try_replace_checked_etag, job);
+ g_vfs_backend_dav_queue_message (backend, msg,
+ try_replace_checked_etag, job);
return TRUE;
}
@@ -2565,8 +2642,8 @@ try_close_write (GVfsBackend *backend,
g_object_unref (stream);
soup_message_body_append (msg->request_body, SOUP_MEMORY_TAKE, data, length);
- soup_session_queue_message (G_VFS_BACKEND_HTTP (backend)->session,
- msg, try_close_write_sent, job);
+ g_vfs_backend_dav_queue_message (backend, msg,
+ try_close_write_sent, job);
return TRUE;
}
@@ -3191,9 +3268,8 @@ push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
g_signal_connect (handle->msg, "wrote-body-data",
G_CALLBACK (push_wrote_body_data), handle);
- soup_session_queue_message (G_VFS_BACKEND_HTTP (handle->backend)->session,
- handle->msg,
- push_done, handle);
+ g_vfs_backend_dav_queue_message (handle->backend, handle->msg,
+ push_done, handle);
}
static void
@@ -3213,9 +3289,8 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data)
g_object_unref (info);
msg = stat_location_begin (handle->uri, FALSE);
- http_backend_queue_message (handle->backend,
- msg,
- push_stat_dest_cb, handle);
+ g_vfs_backend_dav_queue_message (handle->backend, msg,
+ push_stat_dest_cb, handle);
}
else
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]