summaryrefslogtreecommitdiff
path: root/daemon/gvfsftpconnection.c
diff options
context:
space:
mode:
authorRoss Lagerwall <rosslagerwall@gmail.com>2015-02-26 22:30:44 +0000
committerRoss Lagerwall <rosslagerwall@gmail.com>2015-04-09 22:10:36 +0100
commitdccf4aeea3f94251062836f2c6f5f37219c64054 (patch)
treee7a0eaac29a1cb61ce248ab07cb4d502490de917 /daemon/gvfsftpconnection.c
parent7cf5d5ff55fcbdebe0fb7b60e52ecb0c95ca63ee (diff)
downloadgvfs-dccf4aeea3f94251062836f2c6f5f37219c64054.tar.gz
ftp: Implement TLS support
Implement TLS support (aka explicit ftps). This is done by using a different URL scheme, ftps, so that it is only used if explicitly specified. Although the protocol allows transparently upgrading a normal connection to a secure one, there are several problems with this. FEAT is needed to determine support for it but some servers do not allow this before login. Some servers are configured to allow AUTH TLS but have firewalls that block data connections because they can't inspect the traffic. Servers may disallow TLS on the data connection, making it unclear to the user how secure the connection is. Finally, there may be verification errors which need to be presented to the user, and these are unexpected because they did not choose to use ftps. Making secure ftp opt-in as a separate URL scheme side-steps most of these issues as well as ensuring there are no regressions for normal ftp. When using ftps, we assume that the server implements AUTH TLS so the connection is secured before login. It is also assumed that TLS for data connections is allowed, so both control and data connection are secure before any information is transferred. Verification errors are presented during mounting. If the identity changes on subsequent reconnections, the connection is aborted. While presenting verification errors to the user in this way is perhaps not the best way of handling security, it is fairly standard. The implementation has been successfully tested on vsftpd, ProFTPD, Pure-FTPd, IIS, and FileZilla Server. Based on a patch by Benjamin Otte. https://bugzilla.gnome.org/show_bug.cgi?id=526582
Diffstat (limited to 'daemon/gvfsftpconnection.c')
-rw-r--r--daemon/gvfsftpconnection.c133
1 files changed, 127 insertions, 6 deletions
diff --git a/daemon/gvfsftpconnection.c b/daemon/gvfsftpconnection.c
index edfffacf..217d798d 100644
--- a/daemon/gvfsftpconnection.c
+++ b/daemon/gvfsftpconnection.c
@@ -37,6 +37,7 @@ struct _GVfsFtpConnection
GSocketClient * client; /* socket client used for opening connections */
GIOStream * commands; /* ftp command stream */
+ GSocketConnection * connection; /* original connection */
GDataInputStream * commands_in; /* wrapper around in stream to allow line-wise reading */
gboolean waiting_for_reply; /* TRUE if a command was sent but no reply received yet */
@@ -62,6 +63,19 @@ enable_keepalive (GSocketConnection *conn)
g_socket_set_keepalive (g_socket_connection_get_socket (conn), TRUE);
}
+static void
+create_input_stream (GVfsFtpConnection *conn)
+{
+ if (conn->commands_in)
+ {
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (conn->commands_in), FALSE);
+ g_object_unref (conn->commands_in);
+ }
+
+ conn->commands_in = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (conn->commands)));
+ g_data_input_stream_set_newline_type (conn->commands_in, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+}
+
GVfsFtpConnection *
g_vfs_ftp_connection_new (GSocketConnectable *addr,
GCancellable * cancellable,
@@ -85,9 +99,9 @@ g_vfs_ftp_connection_new (GSocketConnectable *addr,
return NULL;
}
- enable_keepalive (G_SOCKET_CONNECTION (conn->commands));
- conn->commands_in = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (conn->commands)));
- g_data_input_stream_set_newline_type (conn->commands_in, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+ conn->connection = G_SOCKET_CONNECTION (conn->commands);
+ enable_keepalive (conn->connection);
+ create_input_stream (conn);
/* The first thing that needs to happen is receiving the welcome message */
conn->waiting_for_reply = TRUE;
@@ -249,7 +263,60 @@ g_vfs_ftp_connection_get_address (GVfsFtpConnection *conn, GError **error)
{
g_return_val_if_fail (conn != NULL, NULL);
- return g_socket_connection_get_remote_address (G_SOCKET_CONNECTION (conn->commands), error);
+ return g_socket_connection_get_remote_address (conn->connection, error);
+}
+
+/**
+ * g_vfs_ftp_connection_data_connection_enable_tls:
+ * @conn: a connection with an active control connection
+ * @server_identity: address of the server used to verify the certificate
+ * @cb: callback called if there's a verification error
+ * @user_data: user data passed to @cb
+ * @cancellable: cancellable to interrupt wait
+ * @error: %NULL or location to take a potential error
+ *
+ * Tries to enable TLS on the given @connection's data connection. If setting
+ * up TLS fails, %FALSE will be returned and @error will be set.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise.
+ **/
+gboolean
+g_vfs_ftp_connection_data_connection_enable_tls (GVfsFtpConnection *conn,
+ GSocketConnectable *server_identity,
+ CertificateCallback cb,
+ gpointer user_data,
+ GCancellable * cancellable,
+ GError ** error)
+{
+ GIOStream *secure;
+
+ g_return_val_if_fail (conn != NULL, FALSE);
+ g_return_val_if_fail (conn->commands != NULL, FALSE);
+
+ secure = g_tls_client_connection_new (conn->data,
+ server_identity,
+ error);
+ if (secure == NULL)
+ return FALSE;
+
+ g_object_unref (conn->data);
+ conn->data = secure;
+
+ g_tls_client_connection_copy_session_state (G_TLS_CLIENT_CONNECTION (secure),
+ G_TLS_CLIENT_CONNECTION (conn->commands));
+
+ g_signal_connect (secure, "accept-certificate", G_CALLBACK (cb), user_data);
+
+ if (!g_tls_connection_handshake (G_TLS_CONNECTION (secure),
+ cancellable,
+ error))
+ {
+ /* Close here to be sure it won't get used anymore */
+ g_io_stream_close (secure, cancellable, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
}
gboolean
@@ -295,7 +362,7 @@ g_vfs_ftp_connection_listen_data_connection (GVfsFtpConnection *conn,
g_vfs_ftp_connection_stop_listening (conn);
- local = g_socket_connection_get_local_address (G_SOCKET_CONNECTION (conn->commands), error);
+ local = g_socket_connection_get_local_address (conn->connection, error);
if (local == NULL)
return NULL;
@@ -480,7 +547,7 @@ g_vfs_ftp_connection_is_usable (GVfsFtpConnection *conn)
return FALSE;
cond = G_IO_ERR | G_IO_HUP | G_IO_IN;
- cond = g_socket_condition_check (g_socket_connection_get_socket (G_SOCKET_CONNECTION (conn->commands)), cond);
+ cond = g_socket_condition_check (g_socket_connection_get_socket (conn->connection), cond);
if (cond)
{
g_debug ("##%2d ## connection unusuable: %s%s%s\r\n", conn->debug_id,
@@ -493,3 +560,57 @@ g_vfs_ftp_connection_is_usable (GVfsFtpConnection *conn)
return TRUE;
}
+/**
+ * g_vfs_ftp_connection_enable_tls:
+ * @conn: a connection without an active data connection
+ * @server_identity: address of the server used to verify the certificate
+ * @cb: callback called if there's a verification error
+ * @user_data: user data passed to @cb
+ * @cancellable: cancellable to interrupt wait
+ * @error: %NULL or location to take a potential error
+ *
+ * Tries to enable TLS on the given @connection. If setting up TLS fails,
+ * %FALSE will be returned and @error will be set. When this function fails,
+ * you need to check if the connection is still usable. It might have been
+ * closed.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise.
+ **/
+gboolean
+g_vfs_ftp_connection_enable_tls (GVfsFtpConnection * conn,
+ GSocketConnectable *server_identity,
+ CertificateCallback cb,
+ gpointer user_data,
+ GCancellable * cancellable,
+ GError ** error)
+{
+ GIOStream *secure;
+
+ g_return_val_if_fail (conn != NULL, FALSE);
+ g_return_val_if_fail (conn->data == NULL, FALSE);
+ g_return_val_if_fail (!conn->waiting_for_reply, FALSE);
+ g_return_val_if_fail (g_buffered_input_stream_get_available (G_BUFFERED_INPUT_STREAM (conn->commands_in)) == 0, FALSE);
+
+ secure = g_tls_client_connection_new (conn->commands,
+ server_identity,
+ error);
+ if (secure == NULL)
+ return FALSE;
+
+ g_object_unref (conn->commands);
+ conn->commands = secure;
+ create_input_stream (conn);
+
+ g_signal_connect (secure, "accept-certificate", G_CALLBACK (cb), user_data);
+
+ if (!g_tls_connection_handshake (G_TLS_CONNECTION (secure),
+ cancellable,
+ error))
+ {
+ /* Close here to be sure it won't get used anymore */
+ g_io_stream_close (secure, cancellable, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}