diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 850a58af4..4676bc02a 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -125,6 +125,15 @@ + + TLS (SSL) support + + + + + + + DNS resolution diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index d6f5aedb2..3574e1ead 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1798,13 +1798,17 @@ g_socket_client_set_local_address g_socket_client_set_protocol g_socket_client_set_socket_type g_socket_client_set_timeout +g_socket_client_set_enable_proxy +g_socket_client_set_tls +g_socket_client_set_tls_validation_flags g_socket_client_get_family g_socket_client_get_local_address g_socket_client_get_protocol g_socket_client_get_socket_type g_socket_client_get_timeout g_socket_client_get_enable_proxy -g_socket_client_set_enable_proxy +g_socket_client_get_tls +g_socket_client_get_tls_validation_flags GSocketClientClass G_IS_SOCKET_CLIENT @@ -2994,3 +2998,126 @@ G_TYPE_POLLABLE_OUTPUT_STREAM g_pollable_output_stream_get_type + +
+gtls +G_TLS_ERROR +GTlsError + +GTlsAuthenticationMode +GTlsCertificateFlags +
+ +
+gtlsbackend +GTlsBackend</FILE> +G_TLS_BACKEND_EXTENSION_POINT_NAME +GTlsBackend +GTlsBackendInterface +g_tls_backend_get_default +g_tls_backend_supports_tls +g_tls_backend_get_certificate_type +g_tls_backend_get_client_connection_type +g_tls_backend_get_server_connection_type +<SUBSECTION Standard> +G_IS_TLS_BACKEND +G_TLS_BACKEND +G_TLS_BACKEND_GET_INTERFACE +G_TYPE_TLS_BACKEND +g_tls_error_quark +<SUBSECTION Private> +g_tls_backend_get_type +</SECTION> + +<SECTION> +<FILE>gtlscertificate</FILE> +<TITLE>GTlsCertificate +GTlsCertificate +g_tls_certificate_new +g_tls_certificate_new_from_pem +g_tls_certificate_new_from_file +g_tls_certificate_new_from_files +g_tls_certificate_list_new_from_file +g_tls_certificate_get_issuer + +GTlsCertificateClass +GTlsCertificatePrivate +G_IS_TLS_CERTIFICATE +G_IS_TLS_CERTIFICATE_CLASS +G_TLS_CERTIFICATE +G_TLS_CERTIFICATE_CLASS +G_TLS_CERTIFICATE_GET_CLASS +G_TYPE_TLS_CERTIFICATE + +g_tls_certificate_get_type +
+ +
+gtlsconnection +GTlsConnection +GTlsConnection +g_tls_connection_set_certificate +g_tls_connection_get_certificate +g_tls_connection_get_peer_certificate +g_tls_connection_set_require_close_notify +g_tls_connection_get_require_close_notify +GTlsRehandshakeMode +g_tls_connection_set_rehandshake_mode +g_tls_connection_get_rehandshake_mode + +g_tls_connection_handshake +g_tls_connection_handshake_async +g_tls_connection_handshake_finish + +g_tls_connection_set_peer_certificate +g_tls_connection_emit_accept_certificate +g_tls_connection_emit_need_certificate + +GTlsConnectionClass +GTlsConnectionPrivate +G_IS_TLS_CONNECTION +G_IS_TLS_CONNECTION_CLASS +G_TLS_CONNECTION +G_TLS_CONNECTION_CLASS +G_TLS_CONNECTION_GET_CLASS +G_TYPE_TLS_CONNECTION + +g_tls_connection_get_type +
+ +
+gtlsclientconnection +GTlsClientConnection +GTlsClientConnection +GTlsClientConnectionInterface +g_tls_client_connection_new +g_tls_client_connection_set_server_identity +g_tls_client_connection_get_server_identity +g_tls_client_connection_set_validation_flags +g_tls_client_connection_get_validation_flags +g_tls_client_connection_set_use_ssl3 +g_tls_client_connection_get_use_ssl3 +g_tls_client_connection_get_accepted_cas + +G_IS_TLS_CLIENT_CONNECTION +G_TLS_CLIENT_CONNECTION +G_TLS_CLIENT_CONNECTION_GET_INTERFACE +G_TYPE_TLS_CLIENT_CONNECTION + +g_tls_client_connection_get_type +
+ +
+gtlsserverconnection +GTlsServerConnection +GTlsServerConnection +GTlsServerConnectionInterface +g_tls_server_connection_new + +G_IS_TLS_SERVER_CONNECTION +G_TLS_SERVER_CONNECTION +G_TLS_SERVER_CONNECTION_GET_INTERFACE +G_TYPE_TLS_SERVER_CONNECTION + +g_tls_server_connection_get_type +
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index 44361788c..4b3afe0e7 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -108,6 +108,11 @@ g_tcp_connection_get_type g_tcp_wrapper_connection_get_type g_themed_icon_get_type g_threaded_socket_service_get_type +g_tls_backend_get_type +g_tls_certificate_get_type +g_tls_client_connection_get_type +g_tls_connection_get_type +g_tls_server_connection_get_type g_unix_connection_get_type g_unix_fd_list_get_type g_unix_fd_message_get_type diff --git a/gio/Makefile.am b/gio/Makefile.am index b70583a94..89aa16276 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -202,7 +202,6 @@ platform_libadd += win32/libgiowin32.la platform_deps += win32/libgiowin32.la endif - SUBDIRS += . if HAVE_FAM @@ -307,6 +306,8 @@ libgio_2_0_la_SOURCES = \ gdummyfile.c \ gdummyproxyresolver.c \ gdummyproxyresolver.h \ + gdummytlsbackend.c \ + gdummytlsbackend.h \ gemblem.h \ gemblem.c \ gemblemedicon.h \ @@ -381,6 +382,11 @@ libgio_2_0_la_SOURCES = \ gthemedicon.c \ gthreadedresolver.c \ gthreadedresolver.h \ + gtlsbackend.c \ + gtlscertificate.c \ + gtlsclientconnection.c \ + gtlsconnection.c \ + gtlsserverconnection.c \ gunionvolumemonitor.c \ gunionvolumemonitor.h \ gvfs.c \ @@ -530,6 +536,11 @@ gio_headers = \ gtcpwrapperconnection.h \ gthreadedsocketservice.h\ gthemedicon.h \ + gtlsbackend.h \ + gtlscertificate.h \ + gtlsclientconnection.h \ + gtlsconnection.h \ + gtlsserverconnection.h \ gvfs.h \ gvolume.h \ gvolumemonitor.h \ diff --git a/gio/gdummytlsbackend.c b/gio/gdummytlsbackend.c new file mode 100644 index 000000000..fd55aa9e5 --- /dev/null +++ b/gio/gdummytlsbackend.c @@ -0,0 +1,274 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "gdummytlsbackend.h" + +#include + +#include "gasyncresult.h" +#include "gcancellable.h" +#include "ginitable.h" +#include "gtlsbackend.h" +#include "gtlscertificate.h" +#include "gtlsclientconnection.h" +#include "gtlsserverconnection.h" +#include "gsimpleasyncresult.h" + +#include "giomodule.h" +#include "giomodule-priv.h" + +#include "glibintl.h" + +static GType _g_dummy_tls_certificate_get_type (void); +static GType _g_dummy_tls_connection_get_type (void); + +struct _GDummyTlsBackend { + GObject parent_instance; +}; + +static void g_dummy_tls_backend_iface_init (GTlsBackendInterface *iface); + +#define g_dummy_tls_backend_get_type _g_dummy_tls_backend_get_type +G_DEFINE_TYPE_WITH_CODE (GDummyTlsBackend, g_dummy_tls_backend, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_TLS_BACKEND, + g_dummy_tls_backend_iface_init) + _g_io_modules_ensure_extension_points_registered (); + g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME, + g_define_type_id, + "dummy", + -100)) + +static void +g_dummy_tls_backend_init (GDummyTlsBackend *backend) +{ +} + +static void +g_dummy_tls_backend_class_init (GDummyTlsBackendClass *backend_class) +{ +} + +static void +g_dummy_tls_backend_iface_init (GTlsBackendInterface *iface) +{ + iface->get_certificate_type = _g_dummy_tls_certificate_get_type; + iface->get_client_connection_type = _g_dummy_tls_connection_get_type; + iface->get_server_connection_type = _g_dummy_tls_connection_get_type; +} + +/* Dummy certificate type */ + +typedef struct _GDummyTlsCertificate GDummyTlsCertificate; +typedef struct _GDummyTlsCertificateClass GDummyTlsCertificateClass; + +struct _GDummyTlsCertificate { + GTlsCertificate parent_instance; +}; + +struct _GDummyTlsCertificateClass { + GTlsCertificateClass parent_class; +}; + +enum +{ + PROP_CERTIFICATE_0, + + PROP_CERTIFICATE, + PROP_CERTIFICATE_PEM, + PROP_PRIVATE_KEY, + PROP_PRIVATE_KEY_PEM +}; + +static void g_dummy_tls_certificate_initable_iface_init (GInitableIface *iface); + +#define g_dummy_tls_certificate_get_type _g_dummy_tls_certificate_get_type +G_DEFINE_TYPE_WITH_CODE (GDummyTlsCertificate, g_dummy_tls_certificate, G_TYPE_TLS_CERTIFICATE, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_dummy_tls_certificate_initable_iface_init);) + +static void +g_dummy_tls_certificate_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + /* We need to define this method to make GObject happy, but it will + * never be possible to construct a working GDummyTlsCertificate, so + * it doesn't have to do anything useful. + */ +} + +static void +g_dummy_tls_certificate_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + /* Just ignore all attempts to set properties. */ +} + +static void +g_dummy_tls_certificate_class_init (GDummyTlsCertificateClass *certificate_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (certificate_class); + + gobject_class->get_property = g_dummy_tls_certificate_get_property; + gobject_class->set_property = g_dummy_tls_certificate_set_property; + + g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate"); + g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem"); + g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key"); + g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem"); +} + +static void +g_dummy_tls_certificate_init (GDummyTlsCertificate *certificate) +{ +} + +static gboolean +g_dummy_tls_certificate_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC, + _("TLS support is not available")); + return FALSE; +} + +static void +g_dummy_tls_certificate_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_dummy_tls_certificate_initable_init; +} + +/* Dummy connection type; since GTlsClientConnection and + * GTlsServerConnection are just interfaces, we can implement them + * both on a single object. + */ + +typedef struct _GDummyTlsConnection GDummyTlsConnection; +typedef struct _GDummyTlsConnectionClass GDummyTlsConnectionClass; + +struct _GDummyTlsConnection { + GTlsConnection parent_instance; +}; + +struct _GDummyTlsConnectionClass { + GTlsConnectionClass parent_class; +}; + +enum +{ + PROP_CONNECTION_0, + + PROP_BASE_IO_STREAM, + PROP_REQUIRE_CLOSE_NOTIFY, + PROP_REHANDSHAKE_MODE, + PROP_VALIDATION_FLAGS, + PROP_SERVER_IDENTITY, + PROP_USE_SSL3, + PROP_ACCEPTED_CAS, + PROP_AUTHENTICATION_MODE +}; + +static void g_dummy_tls_connection_initable_iface_init (GInitableIface *iface); + +#define g_dummy_tls_connection_get_type _g_dummy_tls_connection_get_type +G_DEFINE_TYPE_WITH_CODE (GDummyTlsConnection, g_dummy_tls_connection, G_TYPE_TLS_CONNECTION, + G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION, NULL); + G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION, NULL); + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_dummy_tls_connection_initable_iface_init);) + +static void +g_dummy_tls_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ +} + +static void +g_dummy_tls_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ +} + +static gboolean +g_dummy_tls_connection_close (GIOStream *stream, + GCancellable *cancellable, + GError **error) +{ + return TRUE; +} + +static void +g_dummy_tls_connection_class_init (GDummyTlsConnectionClass *connection_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (connection_class); + GIOStreamClass *io_stream_class = G_IO_STREAM_CLASS (connection_class); + + gobject_class->get_property = g_dummy_tls_connection_get_property; + gobject_class->set_property = g_dummy_tls_connection_set_property; + + /* Need to override this because when initable_init fails it will + * dispose the connection, which will close it, which would + * otherwise try to close its input/output streams, which don't + * exist. + */ + io_stream_class->close_fn = g_dummy_tls_connection_close; + + g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream"); + g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify"); + g_object_class_override_property (gobject_class, PROP_REHANDSHAKE_MODE, "rehandshake-mode"); + g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags"); + g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity"); + g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3"); + g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas"); + g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode"); + +} + +static void +g_dummy_tls_connection_init (GDummyTlsConnection *connection) +{ +} + +static gboolean +g_dummy_tls_connection_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC, + _("TLS support is not available")); + return FALSE; +} + +static void +g_dummy_tls_connection_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_dummy_tls_connection_initable_init; +} + diff --git a/gio/gdummytlsbackend.h b/gio/gdummytlsbackend.h new file mode 100644 index 000000000..40b5038b8 --- /dev/null +++ b/gio/gdummytlsbackend.h @@ -0,0 +1,46 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_DUMMY_TLS_BACKEND_H__ +#define __G_DUMMY_TLS_BACKEND_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_DUMMY_TLS_BACKEND (_g_dummy_tls_backend_get_type ()) +#define G_DUMMY_TLS_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DUMMY_TLS_BACKEND, GDummyTlsBackend)) +#define G_DUMMY_TLS_BACKEND_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DUMMY_TLS_BACKEND, GDummyTlsBackendClass)) +#define G_IS_DUMMY_TLS_BACKEND(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DUMMY_TLS_BACKEND)) +#define G_IS_DUMMY_TLS_BACKEND_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DUMMY_TLS_BACKEND)) +#define G_DUMMY_TLS_BACKEND_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DUMMY_TLS_BACKEND, GDummyTlsBackendClass)) + +typedef struct _GDummyTlsBackend GDummyTlsBackend; +typedef struct _GDummyTlsBackendClass GDummyTlsBackendClass; + +struct _GDummyTlsBackendClass { + GObjectClass parent_class; +}; + +GType _g_dummy_tls_backend_get_type (void); + +G_END_DECLS + +#endif /* __G_DUMMY_TLS_BACKEND_H__ */ diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list index 00d16a9f7..8a42ad75b 100644 --- a/gio/gio-marshal.list +++ b/gio/gio-marshal.list @@ -27,3 +27,5 @@ INT:OBJECT VOID:INT64 VOID:UINT64 BOOLEAN:FLAGS +BOOLEAN:OBJECT,FLAGS +OBJECT:VOID diff --git a/gio/gio.h b/gio/gio.h index 714e935d3..306012f43 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -120,6 +120,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include diff --git a/gio/gio.symbols b/gio/gio.symbols index d3e8379a1..c713d1370 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1067,6 +1067,9 @@ g_dbus_signal_flags_get_type G_GNUC_CONST g_dbus_send_message_flags_get_type G_GNUC_CONST g_credentials_type_get_type G_GNUC_CONST g_dbus_message_byte_order_get_type G_GNUC_CONST +g_tls_authentication_mode_get_type G_GNUC_CONST +g_tls_certificate_flags_get_type G_GNUC_CONST +g_tls_rehandshake_mode_get_type G_GNUC_CONST #endif #endif @@ -1371,6 +1374,7 @@ g_socket_control_message_serialize #if IN_HEADER(__G_SOCKET_CLIENT_H__) #if IN_FILE(__G_SOCKET_CLIENT_C__) g_socket_client_get_type G_GNUC_CONST +g_socket_client_add_application_proxy g_socket_client_connect g_socket_client_connect_async g_socket_client_connect_finish @@ -1383,20 +1387,23 @@ g_socket_client_connect_to_service_finish g_socket_client_connect_to_uri g_socket_client_connect_to_uri_async g_socket_client_connect_to_uri_finish +g_socket_client_get_enable_proxy g_socket_client_get_family g_socket_client_get_local_address g_socket_client_get_protocol g_socket_client_get_socket_type g_socket_client_get_timeout -g_socket_client_get_enable_proxy +g_socket_client_get_tls +g_socket_client_get_tls_validation_flags g_socket_client_new +g_socket_client_set_enable_proxy g_socket_client_set_family g_socket_client_set_local_address g_socket_client_set_protocol g_socket_client_set_socket_type g_socket_client_set_timeout -g_socket_client_set_enable_proxy -g_socket_client_add_application_proxy +g_socket_client_set_tls +g_socket_client_set_tls_validation_flags #endif #endif @@ -2002,3 +2009,67 @@ g_tcp_wrapper_connection_get_base_io_stream g_tcp_wrapper_connection_new #endif #endif + +#if IN_HEADER(__G_TLS_BACKEND_H__) +#if IN_FILE(__G_TLS_BACKEND_C__) +g_tls_backend_get_certificate_type +g_tls_backend_get_client_connection_type +g_tls_backend_get_default +g_tls_backend_get_server_connection_type +g_tls_backend_get_type G_GNUC_CONST +g_tls_backend_supports_tls +g_tls_error_get_type G_GNUC_CONST +g_tls_error_quark +#endif +#endif + +#if IN_HEADER(__G_TLS_CERTIFICATE_H__) +#if IN_FILE(__G_TLS_CERTIFICATE_C__) +g_tls_certificate_get_issuer +g_tls_certificate_get_type G_GNUC_CONST +g_tls_certificate_list_new_from_file +g_tls_certificate_new_from_file +g_tls_certificate_new_from_files +g_tls_certificate_new_from_pem +#endif +#endif + +#if IN_HEADER(__G_TLS_CONNECTION_H__) +#if IN_FILE(__G_TLS_CONNECTION_C__) +g_tls_connection_emit_accept_certificate +g_tls_connection_emit_need_certificate +g_tls_connection_get_certificate +g_tls_connection_get_peer_certificate +g_tls_connection_get_rehandshake_mode +g_tls_connection_get_require_close_notify +g_tls_connection_get_type G_GNUC_CONST +g_tls_connection_handshake +g_tls_connection_handshake_async +g_tls_connection_handshake_finish +g_tls_connection_set_certificate +g_tls_connection_set_peer_certificate +g_tls_connection_set_rehandshake_mode +g_tls_connection_set_require_close_notify +#endif +#endif + +#if IN_HEADER(__G_TLS_CLIENT_CONNECTION_H__) +#if IN_FILE(__G_TLS_CLIENT_CONNECTION_C__) +g_tls_client_connection_get_accepted_cas +g_tls_client_connection_get_server_identity +g_tls_client_connection_get_type G_GNUC_CONST +g_tls_client_connection_get_use_ssl3 +g_tls_client_connection_get_validation_flags +g_tls_client_connection_new +g_tls_client_connection_set_server_identity +g_tls_client_connection_set_use_ssl3 +g_tls_client_connection_set_validation_flags +#endif +#endif + +#if IN_HEADER(__G_TLS_SERVER_CONNECTION_H__) +#if IN_FILE(__G_TLS_SERVER_CONNECTION_C__) +g_tls_server_connection_get_type G_GNUC_CONST +g_tls_server_connection_new +#endif +#endif diff --git a/gio/gioenums.h b/gio/gioenums.h index d534eed14..039b5dc50 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1249,6 +1249,106 @@ typedef enum G_APPLICATION_SEND_ENVIRONMENT = (1 << 4) } GApplicationFlags; +/** + * GTlsError: + * @G_TLS_ERROR_MISC: Miscellaneous TLS error + * @G_TLS_ERROR_BAD_CERTIFICATE: A certificate could not be parsed + * @G_TLS_ERROR_NOT_TLS: The TLS handshake failed because the + * peer does not seem to be a TLS server. + * @G_TLS_ERROR_HANDSHAKE: The TLS handshake failed because the + * peer's certificate was not acceptable. + * @G_TLS_ERROR_CERTIFICATE_REQUIRED: The TLS handshake failed because + * the server requested a client-side certificate, but none was + * provided. See #GTlsConnection::need-certificate. + * @G_TLS_ERROR_EOF: The TLS connection was closed without proper + * notice, which may indicate an attack. See + * g_tls_connection_set_require_close_notify(). + * + * An error code used with %G_TLS_ERROR in a #GError returned from a + * TLS-related routine. + * + * Since: 2.28 + */ +typedef enum { + G_TLS_ERROR_MISC, + G_TLS_ERROR_BAD_CERTIFICATE, + G_TLS_ERROR_NOT_TLS, + G_TLS_ERROR_HANDSHAKE, + G_TLS_ERROR_CERTIFICATE_REQUIRED, + G_TLS_ERROR_EOF +} GTlsError; + +/** + * GTlsCertificateFlags: + * @G_TLS_CERTIFICATE_UNKNOWN_CA: The signing certificate authority is + * not known. + * @G_TLS_CERTIFICATE_BAD_IDENTITY: The certificate does not match the + * expected identity of the site that it was retrieved from. + * @G_TLS_CERTIFICATE_NOT_ACTIVATED: The certificate's activation time + * is still in the future + * @G_TLS_CERTIFICATE_EXPIRED: The certificate has expired + * @G_TLS_CERTIFICATE_REVOKED: The certificate has been revoked + * according to the #GTlsContext's certificate revocation list. + * @G_TLS_CERTIFICATE_INSECURE: The certificate's algorithm is + * considered insecure. + * @G_TLS_CERTIFICATE_GENERIC_ERROR: Some other error occurred validating + * the certificate + * @G_TLS_CERTIFICATE_VALIDATE_ALL: the combination of all of the above + * flags + * + * A set of flags describing TLS certification validation. This can be + * used to set which validation steps to perform (eg, with + * g_tls_client_connection_set_validation_flags()), or to describe why + * a particular certificate was rejected (eg, in + * #GTlsConnection::accept-certificate). + * + * Since: 2.28 + */ +typedef enum { + G_TLS_CERTIFICATE_UNKNOWN_CA = (1 << 0), + G_TLS_CERTIFICATE_BAD_IDENTITY = (1 << 1), + G_TLS_CERTIFICATE_NOT_ACTIVATED = (1 << 2), + G_TLS_CERTIFICATE_EXPIRED = (1 << 3), + G_TLS_CERTIFICATE_REVOKED = (1 << 4), + G_TLS_CERTIFICATE_INSECURE = (1 << 5), + G_TLS_CERTIFICATE_GENERIC_ERROR = (1 << 6), + + G_TLS_CERTIFICATE_VALIDATE_ALL = 0x007f +} GTlsCertificateFlags; + +/** + * GTlsAuthenticationMode: + * @G_TLS_AUTHENTICATION_NONE: client authentication not required + * @G_TLS_AUTHENTICATION_REQUESTED: client authentication is requested + * @G_TLS_AUTHENTICATION_REQUIRED: client authentication is required + * + * The client authentication mode for a #GTlsServerConnection. + * + * Since: 2.28 + */ +typedef enum { + G_TLS_AUTHENTICATION_NONE, + G_TLS_AUTHENTICATION_REQUESTED, + G_TLS_AUTHENTICATION_REQUIRED +} GTlsAuthenticationMode; + +/** + * GTlsRehandshakeMode: + * @G_TLS_REHANDSHAKE_NEVER: Never allow rehandshaking + * @G_TLS_REHANDSHAKE_SAFELY: Allow safe rehandshaking only + * @G_TLS_REHANDSHAKE_UNSAFELY: Allow unsafe rehandshaking + * + * When to allow rehandshaking. See + * g_tls_connection_set_rehandshake_mode(). + * + * Since: 2.28 + */ +typedef enum { + G_TLS_REHANDSHAKE_NEVER, + G_TLS_REHANDSHAKE_SAFELY, + G_TLS_REHANDSHAKE_UNSAFELY +} GTlsRehandshakeMode; + G_END_DECLS #endif /* __GIO_ENUMS_H__ */ diff --git a/gio/giomodule.c b/gio/giomodule.c index d1318933f..ecdaab4da 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -35,6 +35,7 @@ #include "gsocks4proxy.h" #include "gsocks4aproxy.h" #include "gsocks5proxy.h" +#include "gtlsbackend.h" #include "gvfs.h" #ifdef G_OS_WIN32 #include "gregistrysettingsbackend.h" @@ -480,6 +481,7 @@ extern GType g_win32_directory_monitor_get_type (void); extern GType _g_winhttp_vfs_get_type (void); extern GType _g_dummy_proxy_resolver_get_type (void); +extern GType _g_dummy_tls_backend_get_type (void); #ifdef G_PLATFORM_WIN32 @@ -556,6 +558,9 @@ _g_io_modules_ensure_extension_points_registered (void) ep = g_io_extension_point_register (G_PROXY_EXTENSION_POINT_NAME); g_io_extension_point_set_required_type (ep, G_TYPE_PROXY); + + ep = g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME); + g_io_extension_point_set_required_type (ep, G_TYPE_TLS_BACKEND); } G_UNLOCK (registered_extensions); @@ -618,6 +623,7 @@ _g_io_modules_ensure_loaded (void) _g_socks4a_proxy_get_type (); _g_socks4_proxy_get_type (); _g_socks5_proxy_get_type (); + _g_dummy_tls_backend_get_type (); } G_UNLOCK (loaded_dirs); diff --git a/gio/giotypes.h b/gio/giotypes.h index fef1218bf..1c350837a 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -109,6 +109,7 @@ typedef struct _GIOExtension GIOExtension; * Opaque class for definining and scheduling IO jobs. **/ typedef struct _GIOSchedulerJob GIOSchedulerJob; +typedef struct _GIOStreamAdapter GIOStreamAdapter; typedef struct _GLoadableIcon GLoadableIcon; /* Dummy typedef */ typedef struct _GMemoryInputStream GMemoryInputStream; typedef struct _GMemoryOutputStream GMemoryOutputStream; @@ -202,6 +203,13 @@ typedef struct _GTcpWrapperConnection GTcpWrapperConnectio **/ typedef struct _GThreadedSocketService GThreadedSocketService; typedef struct _GThemedIcon GThemedIcon; +typedef struct _GTlsCertificate GTlsCertificate; +typedef struct _GTlsClientConnection GTlsClientConnection; /* Dummy typedef */ +typedef struct _GTlsClientContext GTlsClientContext; /* Dummy typedef */ +typedef struct _GTlsConnection GTlsConnection; +typedef struct _GTlsContext GTlsContext; +typedef struct _GTlsServerConnection GTlsServerConnection; /* Dummy typedef */ +typedef struct _GTlsServerContext GTlsServerContext; /* Dummy typedef */ typedef struct _GVfs GVfs; /* Dummy typedef */ /** diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c index 30e677abe..046432279 100644 --- a/gio/gsocketclient.c +++ b/gio/gsocketclient.c @@ -40,11 +40,12 @@ #include #include #include -#include #include #include #include #include +#include +#include #include "glibintl.h" @@ -80,6 +81,8 @@ enum PROP_LOCAL_ADDRESS, PROP_TIMEOUT, PROP_ENABLE_PROXY, + PROP_TLS, + PROP_TLS_VALIDATION_FLAGS }; struct _GSocketClientPrivate @@ -91,6 +94,8 @@ struct _GSocketClientPrivate guint timeout; gboolean enable_proxy; GHashTable *app_proxies; + gboolean tls; + GTlsCertificateFlags tls_validation_flags; }; static GSocket * @@ -219,6 +224,14 @@ g_socket_client_get_property (GObject *object, g_value_set_boolean (value, client->priv->enable_proxy); break; + case PROP_TLS: + g_value_set_boolean (value, g_socket_client_get_tls (client)); + break; + + case PROP_TLS_VALIDATION_FLAGS: + g_value_set_flags (value, g_socket_client_get_tls_validation_flags (client)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -258,6 +271,14 @@ g_socket_client_set_property (GObject *object, g_socket_client_set_enable_proxy (client, g_value_get_boolean (value)); break; + case PROP_TLS: + g_socket_client_set_tls (client, g_value_get_boolean (value)); + break; + + case PROP_TLS_VALIDATION_FLAGS: + g_socket_client_set_tls_validation_flags (client, g_value_get_flags (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -526,6 +547,91 @@ g_socket_client_set_enable_proxy (GSocketClient *client, g_object_notify (G_OBJECT (client), "enable-proxy"); } +/** + * g_socket_client_get_tls: + * @client: a #GSocketClient. + * + * Gets whether @client creates TLS connections. See + * g_socket_client_set_tls() for details. + * + * Returns: whether @client uses TLS + * + * Since: 2.28 + */ +gboolean +g_socket_client_get_tls (GSocketClient *client) +{ + return client->priv->tls; +} + +/** + * g_socket_client_set_tls: + * @client: a #GSocketClient. + * @tls: whether to use TLS + * + * Sets whether @client creates TLS (aka SSL) connections. If @tls is + * %TRUE, @client will wrap its connections in a #GTlsClientConnection + * and perform a TLS handshake when connecting. + * + * Note that since #GSocketClient must return a #GSocketConnection, + * but #GTlsClientConnection is not a #GSocketConnection, this + * actually wraps the resulting #GTlsClientConnection in a + * #GTcpWrapperConnection when returning it. You can use + * g_tcp_wrapper_connection_get_base_io_stream() on the return value + * to extract the #GTlsClientConnection. + * + * Since: 2.28 + */ +void +g_socket_client_set_tls (GSocketClient *client, + gboolean tls) +{ + tls = !!tls; + if (tls == client->priv->tls) + return; + + client->priv->tls = tls; + g_object_notify (G_OBJECT (client), "tls"); +} + +/** + * g_socket_client_get_tls_validation_flags: + * @client: a #GSocketClient. + * + * Gets the TLS validation flags used creating TLS connections via + * @client. + * + * Returns: the TLS validation flags + * + * Since: 2.28 + */ +GTlsCertificateFlags +g_socket_client_get_tls_validation_flags (GSocketClient *client) +{ + return client->priv->tls_validation_flags; +} + +/** + * g_socket_client_set_tls_validation_flags: + * @client: a #GSocketClient. + * @flags: the validation flags + * + * Sets the TLS validation flags used when creating TLS connections + * via @client. The default value is %G_TLS_CERTIFICATE_VALIDATE_ALL. + * + * Since: 2.28 + */ +void +g_socket_client_set_tls_validation_flags (GSocketClient *client, + GTlsCertificateFlags flags) +{ + if (client->priv->tls_validation_flags != flags) + { + client->priv->tls_validation_flags = flags; + g_object_notify (G_OBJECT (client), "tls-validation-flags"); + } +} + static void g_socket_client_class_init (GSocketClientClass *class) { @@ -594,6 +700,23 @@ g_socket_client_class_init (GSocketClientClass *class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_TLS, + g_param_spec_boolean ("tls", + P_("TLS"), + P_("Whether to create TLS connections"), + FALSE, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_TLS_VALIDATION_FLAGS, + g_param_spec_flags ("tls-validation-flags", + P_("TLS validation flags"), + P_("TLS validation flags to use"), + G_TYPE_TLS_CERTIFICATE_FLAGS, + G_TLS_CERTIFICATE_VALIDATE_ALL, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } /** @@ -632,7 +755,7 @@ g_socket_client_connect (GSocketClient *client, GCancellable *cancellable, GError **error) { - GSocketConnection *connection = NULL; + GIOStream *connection = NULL; GSocketAddressEnumerator *enumerator = NULL; GError *last_error, *tmp_error; @@ -687,7 +810,7 @@ g_socket_client_connect (GSocketClient *client, } if (g_socket_connect (socket, address, cancellable, &last_error)) - connection = g_socket_connection_factory_create_connection (socket); + connection = (GIOStream *)g_socket_connection_factory_create_connection (socket); if (connection && G_IS_PROXY_ADDRESS (address) && @@ -717,31 +840,16 @@ g_socket_client_connect (GSocketClient *client, } else if (proxy) { - GIOStream *io_stream; + GIOStream *proxy_connection; - io_stream = g_proxy_connect (proxy, - G_IO_STREAM (connection), - proxy_addr, - cancellable, - &last_error); + proxy_connection = g_proxy_connect (proxy, + connection, + proxy_addr, + cancellable, + &last_error); g_object_unref (connection); + connection = proxy_connection; g_object_unref (proxy); - - if (io_stream) - { - if (G_IS_SOCKET_CONNECTION (io_stream)) - connection = G_SOCKET_CONNECTION (io_stream); - else - { - connection = g_tcp_wrapper_connection_new (G_POLLABLE_IO_STREAM (io_stream), - socket); - g_object_unref (io_stream); - } - } - else - { - connection = NULL; - } } else if (!g_hash_table_lookup_extended (client->priv->app_proxies, protocol, NULL, NULL)) @@ -754,12 +862,41 @@ g_socket_client_connect (GSocketClient *client, } } + if (connection && client->priv->tls) + { + GTlsClientConnection *tlsconn; + + tlsconn = g_tls_client_connection_new (connection, connectable, &last_error); + g_object_unref (connection); + connection = (GIOStream *)tlsconn; + + if (tlsconn) + { + g_tls_client_connection_set_validation_flags (tlsconn, client->priv->tls_validation_flags); + if (!g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn), + cancellable, &last_error)) + { + g_object_unref (tlsconn); + connection = NULL; + } + } + } + + if (connection && !G_IS_SOCKET_CONNECTION (connection)) + { + GSocketConnection *wrapper_connection; + + wrapper_connection = g_tcp_wrapper_connection_new (connection, socket); + g_object_unref (connection); + connection = (GIOStream *)wrapper_connection; + } + g_object_unref (socket); g_object_unref (address); } g_object_unref (enumerator); - return connection; + return G_SOCKET_CONNECTION (connection); } /** @@ -927,10 +1064,11 @@ typedef struct GCancellable *cancellable; GSocketClient *client; + GSocketConnectable *connectable; GSocketAddressEnumerator *enumerator; GProxyAddress *proxy_addr; GSocket *current_socket; - GSocketConnection *connection; + GIOStream *connection; GError *last_error; } GSocketClientAsyncConnectData; @@ -946,6 +1084,16 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data) { g_assert (data->connection); + if (!G_IS_SOCKET_CONNECTION (data->connection)) + { + GSocketConnection *wrapper_connection; + + wrapper_connection = g_tcp_wrapper_connection_new (data->connection, + data->current_socket); + g_object_unref (data->connection); + data->connection = (GIOStream *)wrapper_connection; + } + g_simple_async_result_set_op_res_gpointer (data->result, data->connection, g_object_unref); @@ -953,6 +1101,7 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data) g_simple_async_result_complete (data->result); g_object_unref (data->result); + g_object_unref (data->connectable); g_object_unref (data->enumerator); if (data->cancellable) g_object_unref (data->cancellable); @@ -987,45 +1136,103 @@ enumerator_next_async (GSocketClientAsyncConnectData *data) } static void -g_socket_client_proxy_connect_callback (GObject *object, +g_socket_client_tls_handshake_callback (GObject *object, GAsyncResult *result, gpointer user_data) { GSocketClientAsyncConnectData *data = user_data; - GIOStream *io_stream; - io_stream = g_proxy_connect_finish (G_PROXY (object), - result, - &data->last_error); - g_object_unref (data->connection); - - if (io_stream) + if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (object), + result, + &data->last_error)) { - if (G_IS_SOCKET_CONNECTION (io_stream)) - data->connection = G_SOCKET_CONNECTION (io_stream); - else - { - data->connection = g_tcp_wrapper_connection_new (G_POLLABLE_IO_STREAM (io_stream), - data->current_socket); - g_object_unref (io_stream); - } + g_object_unref (data->connection); + data->connection = G_IO_STREAM (object); } else { - data->connection = NULL; + g_object_unref (object); g_object_unref (data->current_socket); data->current_socket = NULL; + g_object_unref (data->connection); + data->connection = NULL; + + enumerator_next_async (data); } g_socket_client_async_connect_complete (data); } +static void +g_socket_client_tls_handshake (GSocketClientAsyncConnectData *data) +{ + GTlsClientConnection *tlsconn; + + if (!data->client->priv->tls) + { + g_socket_client_async_connect_complete (data); + return; + } + + tlsconn = g_tls_client_connection_new (data->connection, + data->connectable, + &data->last_error); + if (tlsconn) + { + g_tls_client_connection_set_validation_flags (tlsconn, data->client->priv->tls_validation_flags); + g_tls_connection_handshake_async (G_TLS_CONNECTION (tlsconn), + G_PRIORITY_DEFAULT, + data->cancellable, + g_socket_client_tls_handshake_callback, + data); + } + else + { + g_object_unref (data->current_socket); + data->current_socket = NULL; + g_object_unref (data->connection); + data->connection = NULL; + + enumerator_next_async (data); + } +} + +static void +g_socket_client_proxy_connect_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GSocketClientAsyncConnectData *data = user_data; + + g_object_unref (data->connection); + data->connection = g_proxy_connect_finish (G_PROXY (object), + result, + &data->last_error); + if (!data->connection) + { + g_object_unref (data->current_socket); + data->current_socket = NULL; + + enumerator_next_async (data); + return; + } + + g_socket_client_tls_handshake (data); +} + static void g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data) { GProxy *proxy; - const gchar *protocol = g_proxy_address_get_protocol (data->proxy_addr); + const gchar *protocol; + if (!data->proxy_addr) + { + g_socket_client_tls_handshake (data); + return; + } + + protocol = g_proxy_address_get_protocol (data->proxy_addr); proxy = g_proxy_get_default_for_protocol (protocol); /* The connection should not be anything else then TCP Connection, @@ -1050,7 +1257,7 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data) else if (proxy) { g_proxy_connect_async (proxy, - G_IO_STREAM (data->connection), + data->connection, data->proxy_addr, data->cancellable, g_socket_client_proxy_connect_callback, @@ -1066,6 +1273,8 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data) _("Proxy protocol '%s' is not supported."), protocol); + g_object_unref (data->current_socket); + data->current_socket = NULL; g_object_unref (data->connection); data->connection = NULL; g_object_unref (data->current_socket); @@ -1080,13 +1289,10 @@ g_socket_client_socket_connected (GSocketClientAsyncConnectData *data) { g_socket_set_blocking (data->current_socket, TRUE); - data->connection = + data->connection = (GIOStream *) g_socket_connection_factory_create_connection (data->current_socket); - if (data->proxy_addr) - g_socket_client_proxy_connect (data); - else - g_socket_client_async_connect_complete (data); + g_socket_client_proxy_connect (data); } static gboolean @@ -1243,6 +1449,10 @@ g_socket_client_connect_async (GSocketClient *client, data->client = client; if (cancellable) data->cancellable = g_object_ref (cancellable); + else + data->cancellable = NULL; + data->last_error = NULL; + data->connectable = g_object_ref (connectable); if (can_use_proxy (client)) data->enumerator = g_socket_connectable_proxy_enumerate (connectable); diff --git a/gio/gsocketclient.h b/gio/gsocketclient.h index 2586f707a..180bedd0e 100644 --- a/gio/gsocketclient.h +++ b/gio/gsocketclient.h @@ -89,6 +89,13 @@ gboolean g_socket_client_get_enable_proxy (GSocket void g_socket_client_set_enable_proxy (GSocketClient *client, gboolean enable); +gboolean g_socket_client_get_tls (GSocketClient *client); +void g_socket_client_set_tls (GSocketClient *client, + gboolean tls); +GTlsCertificateFlags g_socket_client_get_tls_validation_flags (GSocketClient *client); +void g_socket_client_set_tls_validation_flags (GSocketClient *client, + GTlsCertificateFlags flags); + GSocketConnection * g_socket_client_connect (GSocketClient *client, GSocketConnectable *connectable, GCancellable *cancellable, diff --git a/gio/gtlsbackend.c b/gio/gtlsbackend.c new file mode 100644 index 000000000..6c93c48a7 --- /dev/null +++ b/gio/gtlsbackend.c @@ -0,0 +1,201 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2010 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "glib.h" + +#include "gtlsbackend.h" +#include "gdummytlsbackend.h" +#include "gioenumtypes.h" +#include "giomodule-priv.h" + +/** + * SECTION:gtls + * @title: TLS Overview + * @short_description: TLS (aka SSL) support for #GSocketConnection + * @include: gio/gio.h + * + * #GTlsConnection and related classes provide TLS (Transport Layer + * Security, previously known as SSL, Secure Sockets Layer) support for + * gio-based network streams. + * + * In the simplest case, for a client connection, you can just set the + * #GSocketClient:tls flag on a #GSocketClient, and then any + * connections created by that client will have TLS negotiated + * automatically, using appropriate default settings, and rejecting + * any invalid or self-signed certificates (unless you change that + * default by setting the #GSocketClient:tls-validation-flags + * property). The returned object will be a #GTcpWrapperConnection, + * which wraps the underlying #GTlsClientConnection. + * + * For greater control, you can create your own #GTlsClientConnection, + * wrapping a #GSocketConnection (or an arbitrary #GIOStream with + * pollable input and output streams) and then connect to its signals, + * such as #GTlsConnection::accept-certificate, before starting the + * handshake. + * + * Server-side TLS is similar, using #GTlsServerConnection. At the + * moment, there is no support for automatically wrapping server-side + * connections in the way #GSocketClient does for client-side + * connections. + */ + +/** + * SECTION:gtlsbackend + * @title: GTlsBackend + * @short_description: TLS backend implementation + * @include: gio/gio.h + */ + +/** + * GTlsBackend: + * + * Type implemented by TLS #GIOModules to provide access to additional + * TLS-related types. + * + * Since: 2.28 + */ + +G_DEFINE_INTERFACE (GTlsBackend, g_tls_backend, G_TYPE_OBJECT); + +static void +g_tls_backend_default_init (GTlsBackendInterface *iface) +{ +} + +static gpointer +get_default_tls_backend (gpointer arg) +{ + const char *use_this; + GList *extensions; + GIOExtensionPoint *ep; + GIOExtension *extension; + + _g_io_modules_ensure_loaded (); + + ep = g_io_extension_point_lookup (G_TLS_BACKEND_EXTENSION_POINT_NAME); + + use_this = g_getenv ("GIO_USE_TLS"); + if (use_this) + { + extension = g_io_extension_point_get_extension_by_name (ep, use_this); + if (extension) + return g_object_new (g_io_extension_get_type (extension), NULL); + } + + extensions = g_io_extension_point_get_extensions (ep); + if (extensions) + { + extension = extensions->data; + return g_object_new (g_io_extension_get_type (extension), NULL); + } + + return NULL; +} + +/** + * g_tls_backend_get_default: + * + * Gets the default #GTlsBackend for the system. + * + * Returns: a #GTlsBackend + * + * Since: 2.28 + */ +GTlsBackend * +g_tls_backend_get_default (void) +{ + static GOnce once_init = G_ONCE_INIT; + + return g_once (&once_init, get_default_tls_backend, NULL); +} + +/** + * g_tls_backend_supports_tls: + * @backend: the #GTlsBackend + * + * Checks if TLS is supported; if this returns %FALSE for the default + * #GTlsBackend, it means no "real" TLS backend is available. + * + * Return value: whether or not TLS is supported + * + * Since: 2.28 + */ +gboolean +g_tls_backend_supports_tls (GTlsBackend *backend) +{ + if (G_TLS_BACKEND_GET_INTERFACE (backend)->supports_tls) + return G_TLS_BACKEND_GET_INTERFACE (backend)->supports_tls (backend); + else if (G_IS_DUMMY_TLS_BACKEND (backend)) + return FALSE; + else + return TRUE; +} + +/** + * g_tls_backend_get_certificate_type: + * @backend: the #GTlsBackend + * + * Gets the #GType of @backend's #GTlsCertificate implementation. + * + * Return value: the #GType of @backend's #GTlsCertificate + * implementation. + * + * Since: 2.28 + */ +GType +g_tls_backend_get_certificate_type (GTlsBackend *backend) +{ + return G_TLS_BACKEND_GET_INTERFACE (backend)->get_certificate_type (); +} + +/** + * g_tls_backend_get_client_connection_type: + * @backend: the #GTlsBackend + * + * Gets the #GType of @backend's #GTlsClientConnection implementation. + * + * Return value: the #GType of @backend's #GTlsClientConnection + * implementation. + * + * Since: 2.28 + */ +GType +g_tls_backend_get_client_connection_type (GTlsBackend *backend) +{ + return G_TLS_BACKEND_GET_INTERFACE (backend)->get_client_connection_type (); +} + +/** + * g_tls_backend_get_server_connection_type: + * @backend: the #GTlsBackend + * + * Gets the #GType of @backend's #GTlsServerConnection implementation. + * + * Return value: the #GType of @backend's #GTlsServerConnection + * implementation. + * + * Since: 2.28 + */ +GType +g_tls_backend_get_server_connection_type (GTlsBackend *backend) +{ + return G_TLS_BACKEND_GET_INTERFACE (backend)->get_server_connection_type (); +} diff --git a/gio/gtlsbackend.h b/gio/gtlsbackend.h new file mode 100644 index 000000000..24ae91122 --- /dev/null +++ b/gio/gtlsbackend.h @@ -0,0 +1,92 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_TLS_BACKEND_H__ +#define __G_TLS_BACKEND_H__ + +#include + +G_BEGIN_DECLS + +/** + * G_TLS_BACKEND_EXTENSION_POINT_NAME: + * + * Extension point for TLS functionality via #GTlsBackend. + * See Extending GIO. + */ +#define G_TLS_BACKEND_EXTENSION_POINT_NAME "gio-tls-backend" + +#define G_TYPE_TLS_BACKEND (g_tls_backend_get_type ()) +#define G_TLS_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_TLS_BACKEND, GTlsBackend)) +#define G_IS_TLS_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_TLS_BACKEND)) +#define G_TLS_BACKEND_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_TLS_BACKEND, GTlsBackendInterface)) + +/** + * GTlsBackend: + * + * TLS (Transport Layer Security, aka SSL) backend. This is an + * internal type used to coordinate the different classes implemented + * by a TLS backend. + * + * Since: 2.28 + */ + +typedef struct _GTlsBackend GTlsBackend; +typedef struct _GTlsBackendInterface GTlsBackendInterface; + +/** + * GTlsBackendInterface: + * @g_iface: The parent interface. + * @get_certificate_type: returns the #GTlsCertificate implementation type + * @get_client_connection_type: returns the #GTlsClientConnection implementation type + * @get_server_connection_type: returns the #GTlsServerConnection implementation type + * + * Provides an interface for describing TLS-related types. + * + * Since: 2.28 + */ +struct _GTlsBackendInterface +{ + GTypeInterface g_iface; + + /* methods */ + gboolean ( *supports_tls) (GTlsBackend *backend); + GType ( *get_certificate_type) (void); + GType ( *get_client_connection_type) (void); + GType ( *get_server_connection_type) (void); +}; + +GType g_tls_backend_get_type (void) G_GNUC_CONST; + +GTlsBackend *g_tls_backend_get_default (void); + +gboolean g_tls_backend_supports_tls (GTlsBackend *backend); + +GType g_tls_backend_get_certificate_type (GTlsBackend *backend); +GType g_tls_backend_get_client_connection_type (GTlsBackend *backend); +GType g_tls_backend_get_server_connection_type (GTlsBackend *backend); + +G_END_DECLS + +#endif /* __G_TLS_BACKEND_H__ */ diff --git a/gio/gtlscertificate.c b/gio/gtlscertificate.c new file mode 100644 index 000000000..876049cb9 --- /dev/null +++ b/gio/gtlscertificate.c @@ -0,0 +1,486 @@ +/* GIO - GLib Input, Output and Certificateing Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "gtlscertificate.h" + +#include +#include "ginitable.h" +#include "gtlsbackend.h" +#include "gtlsconnection.h" +#include "glibintl.h" + +/** + * SECTION: gtlscertificate + * @title: GTlsCertificate + * @short_description: a TLS certificate + * @see_also: #GTlsConnection + * + * A certificate used for TLS authentication and encryption. + * This can represent either a public key only (eg, the certificate + * received by a client from a server), or the combination of + * a public key and a private key (which is needed when acting as a + * #GTlsServerConnection). + * + * Since: 2.28 + */ + +/** + * GTlsCertificate: + * + * Abstract base class for TLS certificate types. + * + * Since: 2.28 + */ + +G_DEFINE_ABSTRACT_TYPE (GTlsCertificate, g_tls_certificate, G_TYPE_OBJECT); + +struct _GTlsCertificatePrivate +{ + GTlsCertificate *issuer; +}; + +enum +{ + PROP_0, + + PROP_CERTIFICATE, + PROP_CERTIFICATE_PEM, + PROP_PRIVATE_KEY, + PROP_PRIVATE_KEY_PEM, + PROP_ISSUER +}; + +static void +g_tls_certificate_init (GTlsCertificate *cert) +{ + cert->priv = G_TYPE_INSTANCE_GET_PRIVATE (cert, + G_TYPE_TLS_CERTIFICATE, + GTlsCertificatePrivate); +} + +static void +g_tls_certificate_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GTlsCertificate *cert = G_TLS_CERTIFICATE (object); + + switch (prop_id) + { + case PROP_ISSUER: + g_value_set_object (value, cert->priv->issuer); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tls_certificate_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTlsCertificate *cert = G_TLS_CERTIFICATE (object); + + switch (prop_id) + { + case PROP_ISSUER: + cert->priv->issuer = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tls_certificate_finalize (GObject *object) +{ + GTlsCertificate *cert = G_TLS_CERTIFICATE (object); + + if (cert->priv->issuer) + g_object_unref (cert->priv->issuer); + + G_OBJECT_CLASS (g_tls_certificate_parent_class)->finalize (object); +} + +static void +g_tls_certificate_class_init (GTlsCertificateClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (GTlsCertificatePrivate)); + + gobject_class->set_property = g_tls_certificate_set_property; + gobject_class->get_property = g_tls_certificate_get_property; + gobject_class->finalize = g_tls_certificate_finalize; + + /** + * GTlsCertificate:certificate: + * + * The DER (binary) encoded representation of the certificate's + * public key. This property and the + * #GTlsCertificate:certificate-pem property represent the same + * data, just in different forms. + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_CERTIFICATE, + g_param_spec_boxed ("certificate", + P_("Certificate"), + P_("The DER representation of the certificate"), + G_TYPE_BYTE_ARRAY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsCertificate:certificate-pem: + * + * The PEM (ASCII) encoded representation of the certificate's + * public key. This property and the #GTlsCertificate:certificate + * property represent the same data, just in different forms. + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_CERTIFICATE_PEM, + g_param_spec_string ("certificate-pem", + P_("Certificate (PEM)"), + P_("The PEM representation of the certificate"), + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsCertificate:private-key: + * + * The DER (binary) encoded representation of the certificate's + * private key. This property (or the + * #GTlsCertificate:private-key-pem property) can be set when + * constructing a key (eg, from a file), but cannot be read. + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY, + g_param_spec_boxed ("private-key", + P_("Private key"), + P_("The DER representation of the certificate's private key"), + G_TYPE_BYTE_ARRAY, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsCertificate:private-key-pem: + * + * The PEM (ASCII) encoded representation of the certificate's + * private key. This property (or the #GTlsCertificate:private-key + * property) can be set when constructing a key (eg, from a file), + * but cannot be read. + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY_PEM, + g_param_spec_string ("private-key-pem", + P_("Private key (PEM)"), + P_("The PEM representation of the certificate's private key"), + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsCertificate:issuer: + * + * A #GTlsCertificate representing the entity that issued this + * certificate. If %NULL, this means that the certificate is either + * self-signed, or else the certificate of the issuer is not + * available. + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_ISSUER, + g_param_spec_object ("issuer", + P_("Issuer"), + P_("The certificate for the issuing entity"), + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static GTlsCertificate * +g_tls_certificate_new_internal (const gchar *certificate_pem, + const gchar *private_key_pem, + GError **error) +{ + GObject *cert; + GTlsBackend *backend; + + backend = g_tls_backend_get_default (); + + cert = g_initable_new (g_tls_backend_get_certificate_type (backend), + NULL, error, + "certificate-pem", certificate_pem, + "private-key-pem", private_key_pem, + NULL); + return G_TLS_CERTIFICATE (cert); +} + +#define PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----" +#define PEM_CERTIFICATE_FOOTER "-----END CERTIFICATE-----" +#define PEM_PRIVKEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" +#define PEM_PRIVKEY_FOOTER "-----END RSA PRIVATE KEY-----" + +static GTlsCertificate * +parse_next_pem_certificate (const gchar **data, + const gchar *data_end, + gboolean required, + GError **error) +{ + const gchar *start, *end, *next; + gchar *cert_pem, *privkey_pem = NULL; + GTlsCertificate *cert; + + start = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER); + if (!start) + { + if (required) + { + g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("No PEM-encoded certificate found")); + } + return NULL; + } + + end = g_strstr_len (start, data_end - start, PEM_CERTIFICATE_FOOTER); + if (!end) + { + g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("Could not parse PEM-encoded certificate")); + return NULL; + } + end += strlen (PEM_CERTIFICATE_FOOTER); + while (*end == '\r' || *end == '\n') + end++; + + cert_pem = g_strndup (start, end - start); + + *data = end; + + next = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER); + start = g_strstr_len (*data, data_end - *data, PEM_PRIVKEY_HEADER); + if (start) + end = g_strstr_len (start, data_end - start, PEM_PRIVKEY_FOOTER); + + if (start && (!next || start < next)) + { + if (!end || (next && end > next)) + { + g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("Could not parse PEM-encoded private key")); + return NULL; + } + + end += strlen (PEM_PRIVKEY_FOOTER); + while (*end == '\r' || *end == '\n') + end++; + + privkey_pem = g_strndup (start, end - start); + + *data = end + strlen (PEM_PRIVKEY_FOOTER); + } + + cert = g_tls_certificate_new_internal (cert_pem, privkey_pem, error); + g_free (cert_pem); + g_free (privkey_pem); + + return cert; +} + +/** + * g_tls_certificate_new_from_pem: + * @data: PEM-encoded certificate data + * @length: the length of @data, or -1 if it's 0-terminated. + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a new #GTlsCertificate from the PEM-encoded data in @data. + * If @data includes both a certificate and a private key, then the + * returned certificate will include the private key data as well. + * + * If @data includes multiple certificates, only the first one will be + * parsed. + * + * Return value: the new certificate, or %NULL if @data is invalid + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_certificate_new_from_pem (const gchar *data, + gssize length, + GError **error) +{ + const gchar *data_end; + + g_return_val_if_fail (data != NULL, NULL); + + if (length == -1) + data_end = data + strlen (data); + else + data_end = data + length; + return parse_next_pem_certificate (&data, data_end, TRUE, error); +} + +/** + * g_tls_certificate_new_from_file: + * @file: file containing a PEM-encoded certificate to import + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a #GTlsCertificate from the PEM-encoded data in @file. If + * @file cannot be read or parsed, the function will return %NULL and + * set @error. Otherwise, this behaves like g_tls_certificate_new(). + * + * Return value: the new certificate, or %NULL on error + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_certificate_new_from_file (const gchar *file, + GError **error) +{ + GTlsCertificate *cert; + gchar *contents; + gsize length; + + if (!g_file_get_contents (file, &contents, &length, error)) + return NULL; + + cert = g_tls_certificate_new_from_pem (contents, length, error); + g_free (contents); + return cert; +} + +/** + * g_tls_certificate_new_from_files: + * @cert_file: file containing a PEM-encoded certificate to import + * @key_file: file containing a PEM-encoded private key to import + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a #GTlsCertificate from the PEM-encoded data in @cert_file + * and @key_file. If either file cannot be read or parsed, the + * function will return %NULL and set @error. Otherwise, this behaves + * like g_tls_certificate_new(). + * + * Return value: the new certificate, or %NULL on error + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_certificate_new_from_files (const gchar *cert_file, + const gchar *key_file, + GError **error) +{ + GTlsCertificate *cert; + gchar *cert_data, *key_data; + + if (!g_file_get_contents (cert_file, &cert_data, NULL, error)) + return NULL; + if (!g_file_get_contents (key_file, &key_data, NULL, error)) + { + g_free (cert_data); + return NULL; + } + + cert = g_tls_certificate_new_internal (cert_data, key_data, error); + g_free (cert_data); + g_free (key_data); + return cert; +} + +/** + * g_tls_certificate_list_new_from_file: + * @file: file containing PEM-encoded certificates to import + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates one or more #GTlsCertificates from the PEM-encoded + * data in @file. If @file cannot be read or parsed, the function will + * return %NULL and set @error. If @file does not contain any + * PEM-encoded certificates, this will return an empty list and not + * set @error. + * + * Return value: (element-type Gio.TlsCertificate) (transfer full): a + * #GList containing #GTlsCertificate objects. You must free the list + * and its contents when you are done with it. + * + * Since: 2.28 + */ +GList * +g_tls_certificate_list_new_from_file (const gchar *file, + GError **error) +{ + GTlsCertificate *cert; + GList *list, *l; + gchar *contents, *end; + const gchar *p; + gsize length; + + if (!g_file_get_contents (file, &contents, &length, error)) + return NULL; + + list = NULL; + end = contents + length; + p = contents; + while (p && *p) + { + cert = parse_next_pem_certificate (&p, end, FALSE, error); + if (!cert) + { + for (l = list; l; l = l->next) + g_object_unref (l->data); + g_list_free (list); + list = NULL; + break; + } + list = g_list_prepend (list, cert); + } + + return g_list_reverse (list); +} + + +/** + * g_tls_certificate_get_issuer: + * @cert: a #GTlsCertificate + * + * Gets the #GTlsCertificate representing @cert's issuer, if known + * + * Return value: (transfer none): The certificate of @cert's issuer, + * or %NULL if @cert is self-signed or signed with an unknown + * certificate. + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_certificate_get_issuer (GTlsCertificate *cert) +{ + return cert->priv->issuer; +} diff --git a/gio/gtlscertificate.h b/gio/gtlscertificate.h new file mode 100644 index 000000000..f8a7fd161 --- /dev/null +++ b/gio/gtlscertificate.h @@ -0,0 +1,75 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_TLS_CERTIFICATE_H__ +#define __G_TLS_CERTIFICATE_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_TLS_CERTIFICATE (g_tls_certificate_get_type ()) +#define G_TLS_CERTIFICATE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CERTIFICATE, GTlsCertificate)) +#define G_TLS_CERTIFICATE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CERTIFICATE, GTlsCertificateClass)) +#define G_IS_TLS_CERTIFICATE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CERTIFICATE)) +#define G_IS_TLS_CERTIFICATE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CERTIFICATE)) +#define G_TLS_CERTIFICATE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CERTIFICATE, GTlsCertificateClass)) + +typedef struct _GTlsCertificateClass GTlsCertificateClass; +typedef struct _GTlsCertificatePrivate GTlsCertificatePrivate; + +struct _GTlsCertificate { + GObject parent_instance; + + GTlsCertificatePrivate *priv; +}; + +struct _GTlsCertificateClass +{ + GObjectClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + gpointer padding[8]; +}; + +GType g_tls_certificate_get_type (void) G_GNUC_CONST; + +GTlsCertificate *g_tls_certificate_new_from_pem (const gchar *data, + gssize length, + GError **error); + +GTlsCertificate *g_tls_certificate_new_from_file (const gchar *file, + GError **error); +GTlsCertificate *g_tls_certificate_new_from_files (const gchar *cert_file, + const gchar *key_file, + GError **error); +GList *g_tls_certificate_list_new_from_file (const gchar *file, + GError **error); + +GTlsCertificate *g_tls_certificate_get_issuer (GTlsCertificate *cert); + +G_END_DECLS + +#endif /* __G_TLS_CERTIFICATE_H__ */ diff --git a/gio/gtlsclientconnection.c b/gio/gtlsclientconnection.c new file mode 100644 index 000000000..1faea96d4 --- /dev/null +++ b/gio/gtlsclientconnection.c @@ -0,0 +1,333 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2010 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "glib.h" + +#include "gtlsclientconnection.h" +#include "ginitable.h" +#include "gioenumtypes.h" +#include "gio-marshal.h" +#include "gsocket.h" +#include "gsocketconnectable.h" +#include "gtlsbackend.h" +#include "gtlscertificate.h" +#include "glibintl.h" + +/** + * SECTION:gtlsclientconnection + * @short_description: TLS client-side connection + * @include: gio/gio.h + * + * #GTlsClientConnection is the client-side subclass of + * #GTlsConnection, representing a client-side TLS connection. + * + * Since: 2.28 + */ + +/** + * GTlsClientConnection: + * + * Abstract base class for the backend-specific client connection + * type. + * + * Since: 2.28 + */ + +G_DEFINE_INTERFACE (GTlsClientConnection, g_tls_client_connection, G_TYPE_TLS_CONNECTION) + +static void +g_tls_client_connection_default_init (GTlsClientConnectionInterface *iface) +{ + /** + * GTlsClientConnection:validation-flags: + * + * What steps to perform when validating a certificate received from + * a server. Server certificates that fail to validate in all of the + * ways indicated here will be rejected unless the application + * overrides the default via #GTlsConnection::accept-certificate. + * + * Since: 2.28 + */ + g_object_interface_install_property (iface, + g_param_spec_flags ("validation-flags", + P_("Validation flags"), + P_("What certificate validation to perform"), + G_TYPE_TLS_CERTIFICATE_FLAGS, + G_TLS_CERTIFICATE_VALIDATE_ALL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * GTlsClientConnection:server-identity: + * + * A #GSocketConnectable describing the identity of the server that + * is expected on the other end of the connection. + * + * If the %G_TLS_CERTIFICATE_BAD_IDENTITY flag is set in + * #GTlsClientConnection:validation-flags, this object will be used + * to determine the expected identify of the remote end of the + * connection; if #GTlsClientConnection:server-identity is not set, + * or does not match the identity presented by the server, then the + * %G_TLS_CERTIFICATE_BAD_IDENTITY validation will fail. + * + * In addition to its use in verifying the server certificate, + * this is also used to give a hint to the server about what + * certificate we expect, which is useful for servers that serve + * virtual hosts. + * + * Since: 2.28 + */ + g_object_interface_install_property (iface, + g_param_spec_object ("server-identity", + P_("Server identity"), + P_("GSocketConnectable identifying the server"), + G_TYPE_SOCKET_CONNECTABLE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * GTlsClientConnection:use-ssl3: + * + * If %TRUE, tells the connection to use SSL 3.0 rather than trying + * to negotiate the best version of TLS or SSL to use. This can be + * used when talking to servers that don't implement version + * negotiation correctly and therefore refuse to handshake at all with + * a "modern" TLS handshake. + * + * Since: 2.28 + */ + g_object_interface_install_property (iface, + g_param_spec_boolean ("use-ssl3", + P_("Use SSL3"), + P_("Use SSL 3.0 rather than trying to use TLS 1.x"), + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * GTlsClientConnection:accepted-cas: + * + * A list of the distinguished names of the Certificate Authorities + * that the server will accept client certificates signed by. If the + * server requests a client certificate during the handshake, then + * this property will be set by the time the + * #GTlsConnection::need-certificate signal is emitted. + * + * Since: 2.28 + */ + g_object_interface_install_property (iface, + g_param_spec_boxed ("accepted-cas", + P_("Accepted CAs"), + P_("Distinguished names of the CAs the server accepts certificates from"), + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +/** + * g_tls_client_connection_new: + * @base_io_stream: the #GIOStream to wrap + * @server_identity: (allow-none): the expected identity of the server + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a new #GTlsClientConnection wrapping @base_io_stream (which + * must have pollable input and output streams) which is assumed to + * communicate with the server identified by @server_identity. + * + * Return value: the new #GTlsClientConnection, or %NULL on error + * + * Since: 2.28 + */ +GTlsClientConnection * +g_tls_client_connection_new (GIOStream *base_io_stream, + GSocketConnectable *server_identity, + GError **error) +{ + GObject *conn; + GTlsBackend *backend; + + backend = g_tls_backend_get_default (); + conn = g_initable_new (g_tls_backend_get_client_connection_type (backend), + NULL, error, + "base-io-stream", base_io_stream, + "server-identity", server_identity, + NULL); + return G_TLS_CLIENT_CONNECTION (conn); +} + +/** + * g_tls_client_connection_get_validation_flags: + * @conn: the #GTlsClientConnection + * + * Gets @conn's validation flags + * + * Return value: the validation flags + * + * Since: 2.28 + */ +GTlsCertificateFlags +g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn) +{ + GTlsCertificateFlags flags = 0; + + g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), 0); + + g_object_get (G_OBJECT (conn), "validation-flags", &flags, NULL); + return flags; +} + +/** + * g_tls_client_connection_set_validation_flags: + * @conn: the #GTlsClientConnection + * @flags: the #GTlsCertificatelags to use + * + * Sets @conn's validation flags, to override the default set of + * checks performed when validating a server certificate. + * + * Since: 2.28 + */ +void +g_tls_client_connection_set_validation_flags (GTlsClientConnection *conn, + GTlsCertificateFlags flags) +{ + g_return_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn)); + + g_object_set (G_OBJECT (conn), "validation-flags", flags, NULL); +} + +/** + * g_tls_client_connection_get_server_identity: + * @conn: the #GTlsClientConnection + * + * Gets @conn's expected server identity + * + * Return value: a #GSocketConnectable describing the + * expected server identity, or %NULL if the expected identity is not + * known. + * + * Since: 2.28 + */ +GSocketConnectable * +g_tls_client_connection_get_server_identity (GTlsClientConnection *conn) +{ + GSocketConnectable *identity = NULL; + + g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), 0); + + g_object_get (G_OBJECT (conn), "server-identity", &identity, NULL); + if (identity) + g_object_unref (identity); + return identity; +} + +/** + * g_tls_client_connection_set_server_identity: + * @conn: the #GTlsClientConnection + * @identity: a #GSocketConnectable describing the expected server identity + * + * Sets @conn's expected server identity, which is used both to tell + * servers on virtual hosts which certificate to present, and also + * to let @conn know what name to look for in the certificate when + * performing %G_TLS_CERTIFICATE_BAD_IDENTITY validation, if enabled. + * + * Since: 2.28 + */ +void +g_tls_client_connection_set_server_identity (GTlsClientConnection *conn, + GSocketConnectable *identity) +{ + g_return_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn)); + + g_object_set (G_OBJECT (conn), "server-identity", identity, NULL); +} + +/** + * g_tls_client_connection_get_use_ssl3: + * @conn: the #GTlsClientConnection + * + * Gets whether @conn will use SSL 3.0 rather than the + * highest-supported version of TLS; see + * g_tls_client_connection_set_use_ssl3(). + * + * Return value: whether @conn will use SSL 3.0 + * + * Since: 2.28 + */ +gboolean +g_tls_client_connection_get_use_ssl3 (GTlsClientConnection *conn) +{ + gboolean use_ssl3 = FALSE; + + g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), 0); + + g_object_get (G_OBJECT (conn), "use-ssl3", &use_ssl3, NULL); + return use_ssl3; +} + +/** + * g_tls_client_connection_set_use_ssl3: + * @conn: the #GTlsClientConnection + * @use_ssl3: whether to use SSL 3.0 + * + * If @use_ssl3 is %TRUE, this forces @conn to use SSL 3.0 rather than + * trying to properly negotiate the right version of TLS or SSL to use. + * This can be used when talking to servers that do not implement the + * fallbacks correctly and which will therefore fail to handshake with + * a "modern" TLS handshake attempt. + * + * Since: 2.28 + */ +void +g_tls_client_connection_set_use_ssl3 (GTlsClientConnection *conn, + gboolean use_ssl3) +{ + g_return_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn)); + + g_object_set (G_OBJECT (conn), "use-ssl3", use_ssl3, NULL); +} + +/** + * g_tls_client_connection_get_accepted_cas: + * @conn: the #GTlsClientConnection + * + * Gets the list of distinguished names of the Certificate Authorities + * that the server will accept certificates from. This will be set + * during the TLS handshake if the server requests a certificate. + * Otherwise, it will be %NULL. + * + * Return value: (transfer full) (array zero-terminated=1): the list + * of CA names, which you must free (eg, with g_strfreev()). + * + * Since: 2.28 + */ +char ** +g_tls_client_connection_get_accepted_cas (GTlsClientConnection *conn) +{ + char **accepted_cas = NULL; + + g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), NULL); + + g_object_get (G_OBJECT (conn), "accepted-cas", &accepted_cas, NULL); + return accepted_cas; +} diff --git a/gio/gtlsclientconnection.h b/gio/gtlsclientconnection.h new file mode 100644 index 000000000..2bfa8c451 --- /dev/null +++ b/gio/gtlsclientconnection.h @@ -0,0 +1,72 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_TLS_CLIENT_CONNECTION_H__ +#define __G_TLS_CLIENT_CONNECTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_TLS_CLIENT_CONNECTION (g_tls_client_connection_get_type ()) +#define G_TLS_CLIENT_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CLIENT_CONNECTION, GTlsClientConnection)) +#define G_IS_TLS_CLIENT_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CLIENT_CONNECTION)) +#define G_TLS_CLIENT_CONNECTION_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), G_TYPE_TLS_CLIENT_CONNECTION, GTlsClientConnectionInterface)) + +/** + * GTlsClientConnection: + * + * TLS client-side connection; the client-side implementation of a + * #GTlsConnection + * + * Since: 2.28 + */ +typedef struct _GTlsClientConnectionInterface GTlsClientConnectionInterface; + +struct _GTlsClientConnectionInterface +{ + GTypeInterface g_iface; + +}; + +GType g_tls_client_connection_get_type (void) G_GNUC_CONST; + +GTlsClientConnection *g_tls_client_connection_new (GIOStream *base_io_stream, + GSocketConnectable *server_identity, + GError **error); + +GTlsCertificateFlags g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn); +void g_tls_client_connection_set_validation_flags (GTlsClientConnection *conn, + GTlsCertificateFlags flags); +GSocketConnectable *g_tls_client_connection_get_server_identity (GTlsClientConnection *conn); +void g_tls_client_connection_set_server_identity (GTlsClientConnection *conn, + GSocketConnectable *identity); +gboolean g_tls_client_connection_get_use_ssl3 (GTlsClientConnection *conn); +void g_tls_client_connection_set_use_ssl3 (GTlsClientConnection *conn, + gboolean use_ssl3); +char ** g_tls_client_connection_get_accepted_cas (GTlsClientConnection *conn); + +G_END_DECLS + +#endif /* __G_TLS_CLIENT_CONNECTION_H__ */ diff --git a/gio/gtlsconnection.c b/gio/gtlsconnection.c new file mode 100644 index 000000000..e9e4250ed --- /dev/null +++ b/gio/gtlsconnection.c @@ -0,0 +1,720 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2010 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "glib.h" + +#include "gtlsconnection.h" +#include "gcancellable.h" +#include "gioenumtypes.h" +#include "gio-marshal.h" +#include "gsocket.h" +#include "gtlsbackend.h" +#include "gtlscertificate.h" +#include "gtlsclientconnection.h" +#include "glibintl.h" + +/** + * SECTION:gtlsconnection + * @short_description: TLS connection type + * @include: gio/gio.h + * + * #GTlsConnection is the base TLS connection class type, which wraps + * a #GIOStream and provides TLS encryption on top of it. Its + * subclasses, #GTlsClientConnection and #GTlsServerConnection, + * implement client-side and server-side TLS, respectively. + * + * Since: 2.28 + */ + +/** + * GTlsConnection: + * + * Abstract base class for the backend-specific #GTlsClientConnection + * and #GTlsServerConnection types. + * + * Since: 2.28 + */ + +G_DEFINE_ABSTRACT_TYPE (GTlsConnection, g_tls_connection, G_TYPE_IO_STREAM) + +static void g_tls_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void g_tls_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void g_tls_connection_finalize (GObject *object); + +static gboolean g_tls_connection_certificate_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy); + +enum { + NEED_CERTIFICATE, + ACCEPT_CERTIFICATE, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + PROP_BASE_IO_STREAM, + PROP_REQUIRE_CLOSE_NOTIFY, + PROP_REHANDSHAKE_MODE, + PROP_CERTIFICATE, + PROP_PEER_CERTIFICATE +}; + +struct _GTlsConnectionPrivate { + GTlsCertificate *certificate, *peer_certificate; +}; + +static void +g_tls_connection_class_init (GTlsConnectionClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GTlsConnectionPrivate)); + + gobject_class->get_property = g_tls_connection_get_property; + gobject_class->set_property = g_tls_connection_set_property; + gobject_class->finalize = g_tls_connection_finalize; + + /** + * GTlsConnection:base-io-stream: + * + * The #GIOStream that the connection wraps + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_BASE_IO_STREAM, + g_param_spec_object ("base-io-stream", + P_("Base IOStream"), + P_("The GIOStream that the connection wraps"), + G_TYPE_IO_STREAM, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsConnection:require-close-notify: + * + * Whether or not proper TLS close notification is required. + * See g_tls_connection_set_require_close_notify(). + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, + g_param_spec_boolean ("require-close-notify", + P_("Require close notify"), + P_("Whether to require proper TLS close notification"), + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsConnection:rehandshake-mode: + * + * The rehandshaking mode. See + * g_tls_connection_set_rehandshake_mode(). + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_REHANDSHAKE_MODE, + g_param_spec_enum ("rehandshake-mode", + P_("Rehandshake mode"), + P_("When to allow rehandshaking"), + G_TYPE_TLS_REHANDSHAKE_MODE, + G_TLS_REHANDSHAKE_SAFELY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsConnection:certificate: + * + * The connection's certificate; see + * g_tls_connection_set_certificate(). + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_CERTIFICATE, + g_param_spec_object ("certificate", + P_("Certificate"), + P_("The connection's certificate"), + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * GTlsConnection:peer-certificate: + * + * The connection's peer's certificate, after it has been set during + * the TLS handshake. + * + * Since: 2.28 + */ + g_object_class_install_property (gobject_class, PROP_PEER_CERTIFICATE, + g_param_spec_object ("peer-certificate", + P_("Peer Certificate"), + P_("The connection's peer's certificate"), + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * GTlsConnection::need-certificate: + * @conn: a #GTlsConnection + * + * Emitted during the TLS handshake if a certificate is needed and + * one has not been set via g_tls_connection_set_certificate(). + * + * For server-side connections, a certificate is always needed, and + * the connection will fail if none is provided. + * + * For client-side connections, the signal will be emitted only if + * the server has requested a certificate; you can call + * g_tls_client_connection_get_accepted_cas() to get a list of + * Certificate Authorities that the server will accept certificates + * from. If you do not return a certificate (and have not provided + * one via g_tls_connection_set_certificate()) then the server may + * reject the handshake, in which case the operation will eventually + * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED. + * + * Note that if this signal is emitted as part of asynchronous I/O + * in the main thread, then you should not attempt to interact with + * the user before returning from the signal handler. If you want to + * let the user choose a certificate to return, you would have to + * return %NULL from the signal handler on the first attempt, and + * then after the connection attempt returns a + * %G_TLS_ERROR_CERTIFICATE_REQUIRED, you can interact with the + * user, create a new connection, and call + * g_tls_connection_set_certificate() on it before handshaking (or + * just connect to the signal again and return the certificate the + * next time). + * + * If you are doing I/O in another thread, you do not + * need to worry about this, and can simply block in the signal + * handler until the UI thread returns an answer. + * + * Return value: the certificate to send to the peer, or %NULL to + * send no certificate. If you return a certificate, the signal + * emission will be stopped and further handlers will not be called. + * + * Since: 2.28 + */ + signals[NEED_CERTIFICATE] = + g_signal_new (I_("need-certificate"), + G_TYPE_TLS_CONNECTION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GTlsConnectionClass, need_certificate), + g_tls_connection_certificate_accumulator, NULL, + _gio_marshal_OBJECT__VOID, + G_TYPE_TLS_CERTIFICATE, 0); + + /** + * GTlsConnection::accept-certificate: + * @conn: a #GTlsConnection + * @peer_cert: the peer's #GTlsCertificate + * @errors: the problems with @peer_cert. + * + * Emitted during the TLS handshake after the peer certificate has + * been received. You can examine @peer_cert's certification path by + * calling g_tls_certificate_get_issuer() on it. + * + * For a client-side connection, @peer_cert is the server's + * certificate, and the signal will only be emitted if the + * certificate was not acceptable according to @conn's + * #GTlsClientConnection:validation_flags. If you would like the + * certificate to be accepted despite @errors, return %TRUE from the + * signal handler. Otherwise, if no handler accepts the certificate, + * the handshake will fail with %G_TLS_ERROR_BAD_CERTIFICATE. + * + * For a server-side connection, @peer_cert is the certificate + * presented by the client, if this was requested via the server's + * #GTlsServerConnection:authentication_mode. On the server side, + * the signal is always emitted when the client presents a + * certificate, and the certificate will only be accepted if a + * handler returns %TRUE. + * + * As with #GTlsConnection::need_certificate, you should not + * interact with the user during the signal emission if the signal + * was emitted as part of an asynchronous operation in the main + * thread. + * + * Return value: %TRUE to accept @peer_cert (which will also + * immediately end the signal emission). %FALSE to allow the signal + * emission to continue, which will cause the handshake to fail if + * no one else overrides it. + * + * Since: 2.28 + */ + signals[ACCEPT_CERTIFICATE] = + g_signal_new (I_("accept-certificate"), + G_TYPE_TLS_CONNECTION, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GTlsConnectionClass, accept_certificate), + g_signal_accumulator_true_handled, NULL, + _gio_marshal_BOOLEAN__OBJECT_FLAGS, + G_TYPE_BOOLEAN, 2, + G_TYPE_TLS_CERTIFICATE, + G_TYPE_TLS_CERTIFICATE_FLAGS); +} + +static void +g_tls_connection_init (GTlsConnection *conn) +{ + conn->priv = G_TYPE_INSTANCE_GET_PRIVATE (conn, G_TYPE_TLS_CONNECTION, GTlsConnectionPrivate); +} + +static void +g_tls_connection_finalize (GObject *object) +{ + GTlsConnection *conn = G_TLS_CONNECTION (object); + + if (conn->priv->certificate) + g_object_unref (conn->priv->certificate); + if (conn->priv->peer_certificate) + g_object_unref (conn->priv->peer_certificate); + + G_OBJECT_CLASS (g_tls_connection_parent_class)->finalize (object); +} + +static void +g_tls_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GTlsConnection *conn = G_TLS_CONNECTION (object); + + switch (prop_id) + { + case PROP_CERTIFICATE: + g_value_set_object (value, conn->priv->certificate); + break; + + case PROP_PEER_CERTIFICATE: + g_value_set_object (value, conn->priv->peer_certificate); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_tls_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTlsConnection *conn = G_TLS_CONNECTION (object); + + switch (prop_id) + { + case PROP_CERTIFICATE: + g_tls_connection_set_certificate (conn, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/** + * g_tls_connection_set_certificate: + * @conn: a #GTlsConnection + * @certificate: the certificate to use for @conn + * + * This sets the certificate that @conn will present to its peer + * during the TLS handshake. If this is not set, + * #GTlsConnection::need-certificate will be emitted during the + * handshake if needed. + * + * Since: 2.28 + */ +void +g_tls_connection_set_certificate (GTlsConnection *conn, + GTlsCertificate *certificate) +{ + g_return_if_fail (G_IS_TLS_CONNECTION (conn)); + g_return_if_fail (G_IS_TLS_CERTIFICATE (certificate)); + + if (conn->priv->certificate) + g_object_unref (conn->priv->certificate); + conn->priv->certificate = certificate ? g_object_ref (certificate) : NULL; + g_object_notify (G_OBJECT (conn), "certificate"); +} + +/** + * g_tls_connection_get_certificate: + * @conn: a #GTlsConnection + * + * Gets @conn's certificate, as set by + * g_tls_connection_set_certificate() or returned from one of the + * signals. + * + * Return value: @conn's certificate, or %NULL + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_connection_get_certificate (GTlsConnection *conn) +{ + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), NULL); + + return conn->priv->certificate; +} + +/** + * g_tls_connection_get_peer_certificate: + * @conn: a #GTlsConnection + * + * Gets @conn's peer's certificate after it has been set during the + * handshake. + * + * Return value: @conn's peer's certificate, or %NULL + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_connection_get_peer_certificate (GTlsConnection *conn) +{ + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), NULL); + + return conn->priv->peer_certificate; +} + +/** + * g_tls_connection_set_require_close_notify: + * @conn: a #GTlsConnection + * @require_close_notify: whether or not to require close notification + * + * Sets whether or not @conn requires a proper TLS close notification + * before closing the connection. If this is %TRUE (the default), then + * calling g_io_stream_close() on @conn will send a TLS close + * notification, and likewise it will expect to receive a close + * notification before the connection is closed when reading, and will + * return a %G_TLS_ERROR_EOF error if the connection is closed without + * proper notification (since this may indicate a network error, or + * man-in-the-middle attack). + * + * In some protocols, the application will know whether or not the + * connection was closed cleanly based on application-level data + * (because the application-level data includes a length field, or is + * somehow self-delimiting); in this case, the close notify is + * redundant and sometimes omitted. (TLS 1.1 explicitly allows this; + * in TLS 1.0 it is technically an error, but often done anyway.) You + * can use g_tls_connection_set_require_close_notify() to tell @conn to + * allow an "unannounced" connection close, in which case it is up to + * the application to check that the data has been fully received. + * + * Since: 2.28 + */ +void +g_tls_connection_set_require_close_notify (GTlsConnection *conn, + gboolean require_close_notify) +{ + g_return_if_fail (G_IS_TLS_CONNECTION (conn)); + + g_object_set (G_OBJECT (conn), + "require-close-notify", require_close_notify, + NULL); +} + +/** + * g_tls_connection_get_require_close_notify: + * @conn: a #GTlsConnection + * + * Tests whether or not @conn requires a proper TLS close notification + * before closing the connection. See + * g_tls_connection_set_require_close_notify() for details. + * + * Return value: %TRUE if @conn requires a proper TLS close + * notification. + * + * Since: 2.28 + */ +gboolean +g_tls_connection_get_require_close_notify (GTlsConnection *conn) +{ + gboolean require_close_notify; + + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), TRUE); + + g_object_get (G_OBJECT (conn), + "require-close-notify", &require_close_notify, + NULL); + return require_close_notify; +} + +/** + * g_tls_connection_set_rehandshake_mode: + * @conn: a #GTlsConnection + * @mode: the rehandshaking mode + * + * Sets how @conn behaves with respect to rehandshaking requests. + * + * %G_TLS_REHANDSHAKE_NEVER means that it will never agree to + * rehandshake after the initial handshake is complete. (For a client, + * this means it will refuse rehandshake requests from the server, and + * for a server, this means it will close the connection with an error + * if the client attempts to rehandshake.) + * + * %G_TLS_REHANDSHAKE_SAFELY means that the connection will allow a + * rehandshake only if the other end of the connection supports the + * TLS renegotiation_info extension. This is the + * default behavior, but means that rehandshaking will not work + * against older implementations that do not support that extension. + * + * %G_TLS_REHANDSHAKE_UNSAFELY means that the connection will allow + * rehandshaking even without the + * renegotiation_info extension. On the server side + * in particular, this is not recommended, since it leaves the server + * open to certain attacks. However, this mode is necessary if you + * need to allow renegotiation with older client software. + * + * Since: 2.28 + */ +void +g_tls_connection_set_rehandshake_mode (GTlsConnection *conn, + GTlsRehandshakeMode mode) +{ + g_return_if_fail (G_IS_TLS_CONNECTION (conn)); + + g_object_set (G_OBJECT (conn), + "rehandshake-mode", mode, + NULL); +} + +/** + * g_tls_connection_get_rehandshake_mode: + * @conn: a #GTlsConnection + * + * Gets @conn rehandshaking mode. See + * g_tls_connection_set_rehandshake() for details. + * + * Return value: @conn's rehandshaking mode + * + * Since: 2.28 + */ +GTlsRehandshakeMode +g_tls_connection_get_rehandshake_mode (GTlsConnection *conn) +{ + GTlsRehandshakeMode mode; + + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), G_TLS_REHANDSHAKE_NEVER); + + g_object_get (G_OBJECT (conn), + "rehandshake-mode", &mode, + NULL); + return mode; +} + +/** + * g_tls_connection_handshake: + * @conn: a #GTlsConnection + * @cancellable: a #GCancellable, or %NULL + * @error: a #GError, or %NULL + * + * Attempts a TLS handshake on @conn. + * + * On the client side, it is never necessary to call this method; + * although the connection needs to perform a handshake after + * connecting (or after sending a "STARTTLS"-type command) and may + * need to rehandshake later if the server requests it, + * #GTlsConnection will handle this for you automatically when you try + * to send or receive data on the connection. However, you can call + * g_tls_connection_handshake() manually if you want to know for sure + * whether the initial handshake succeeded or failed (as opposed to + * just immediately trying to write to @conn's output stream, in which + * case if it fails, it may not be possible to tell if it failed + * before or after completing the handshake). + * + * Likewise, on the server side, although a handshake is necessary at + * the beginning of the communication, you do not need to call this + * function explicitly unless you want clearer error reporting. + * However, you may call g_tls_connection_handshake() later on to + * renegotiate parameters (encryption methods, etc) with the client. + * + * #GTlsConnection::accept_certificate and + * #GTlsConnection::need_certificate may be emitted during the + * handshake. + * + * Return value: success or failure + * + * Since: 2.28 + */ +gboolean +g_tls_connection_handshake (GTlsConnection *conn, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), FALSE); + + return G_TLS_CONNECTION_GET_CLASS (conn)->handshake (conn, cancellable, error); +} + +/** + * g_tls_connection_handshake_async: + * @conn: a #GTlsConnection + * @io_priority: the I/O priority + * of the request. + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the handshake is complete + * @user_data: the data to pass to the callback function + * + * Asynchronously performs a TLS handshake on @conn. See + * g_tls_connection_handshake() for more information. + * + * Since: 2.28 + */ +void +g_tls_connection_handshake_async (GTlsConnection *conn, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (G_IS_TLS_CONNECTION (conn)); + + return G_TLS_CONNECTION_GET_CLASS (conn)->handshake_async (conn, io_priority, + cancellable, + callback, user_data); +} + +/** + * g_tls_connection_handshake_finish: + * @conn: a #GTlsConnection + * @result: a #GAsyncResult. + * @error: a #GError pointer, or %NULL + * + * Finish an asynchronous TLS handshake operation. See + * g_tls_connection_handshake() for more information. + * + * Return value: %TRUE on success, %FALSE on failure, in which + * case @error will be set. + * + * Since: 2.28 + */ +gboolean +g_tls_connection_handshake_finish (GTlsConnection *conn, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), FALSE); + + return G_TLS_CONNECTION_GET_CLASS (conn)->handshake_finish (conn, result, error); +} + +/** + * g_tls_error_quark: + * + * Gets the TLS error quark. + * + * Return value: a #GQuark. + * + * Since: 2.28 + */ +GQuark +g_tls_error_quark (void) +{ + return g_quark_from_static_string ("g-tls-error-quark"); +} + + +static gboolean +g_tls_connection_certificate_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + GTlsCertificate *cert; + + cert = g_value_get_object (handler_return); + if (cert) + g_value_set_object (return_accu, cert); + + return cert != NULL; +} + +/** + * g_tls_connection_emit_need_certificate: + * @conn: a #GTlsConnection + * + * Used by #GTlsConnection implementations to emit the + * #GTlsConnection::need-certificate signal. + * + * Since: 2.28 + */ +GTlsCertificate * +g_tls_connection_emit_need_certificate (GTlsConnection *conn) +{ + GTlsCertificate *cert = NULL; + + g_signal_emit (conn, signals[NEED_CERTIFICATE], 0, + &cert); + return cert; +} + +/** + * g_tls_connection_emit_accept_certificate: + * @conn: a #GTlsConnection + * + * Used by #GTlsConnection implementations to emit the + * #GTlsConnection::accept-certificate signal. + * + * Since: 2.28 + */ +gboolean +g_tls_connection_emit_accept_certificate (GTlsConnection *conn, + GTlsCertificate *peer_cert, + GTlsCertificateFlags errors) +{ + gboolean accept = FALSE; + + g_signal_emit (conn, signals[ACCEPT_CERTIFICATE], 0, + peer_cert, errors, &accept); + return accept; +} + +/** + * g_tls_connection_set_peer_certificate: + * @conn: a #GTlsConnection + * @certificate: the peer certificate + * + * Used by #GTlsConnection implementations to set the connection's + * peer certificate. + * + * Since: 2.28 + */ +void +g_tls_connection_set_peer_certificate (GTlsConnection *conn, + GTlsCertificate *certificate) +{ + if (conn->priv->peer_certificate) + g_object_unref (conn->priv->peer_certificate); + conn->priv->peer_certificate = certificate ? g_object_ref (certificate) : NULL; + g_object_notify (G_OBJECT (conn), "peer-certificate"); +} diff --git a/gio/gtlsconnection.h b/gio/gtlsconnection.h new file mode 100644 index 000000000..86f1f68f5 --- /dev/null +++ b/gio/gtlsconnection.h @@ -0,0 +1,137 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_TLS_CONNECTION_H__ +#define __G_TLS_CONNECTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_TLS_CONNECTION (g_tls_connection_get_type ()) +#define G_TLS_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CONNECTION, GTlsConnection)) +#define G_TLS_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION, GTlsConnectionClass)) +#define G_IS_TLS_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CONNECTION)) +#define G_IS_TLS_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION)) +#define G_TLS_CONNECTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CONNECTION, GTlsConnectionClass)) + +/** + * GTlsConnection: + * + * TLS connection. This is an abstract type that will be subclassed by + * a TLS-library-specific subtype. + * + * Since: 2.28 + */ +typedef struct _GTlsConnectionClass GTlsConnectionClass; +typedef struct _GTlsConnectionPrivate GTlsConnectionPrivate; + +struct _GTlsConnection { + GIOStream parent_instance; + + GTlsConnectionPrivate *priv; +}; + +struct _GTlsConnectionClass +{ + GIOStreamClass parent_class; + + /* signals */ + GTlsCertificate * ( *need_certificate) (GTlsConnection *connection); + + gboolean ( *accept_certificate) (GTlsConnection *connection, + GTlsCertificate *peer_cert, + GTlsCertificateFlags errors); + + /* methods */ + gboolean ( *handshake ) (GTlsConnection *conn, + GCancellable *cancellable, + GError **error); + + void ( *handshake_async ) (GTlsConnection *conn, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean ( *handshake_finish ) (GTlsConnection *conn, + GAsyncResult *result, + GError **error); + + /*< private >*/ + /* Padding for future expansion */ + gpointer padding[8]; +}; + +GType g_tls_connection_get_type (void) G_GNUC_CONST; + +void g_tls_connection_set_certificate (GTlsConnection *conn, + GTlsCertificate *certificate); +GTlsCertificate *g_tls_connection_get_certificate (GTlsConnection *conn); + +GTlsCertificate *g_tls_connection_get_peer_certificate (GTlsConnection *conn); + +void g_tls_connection_set_require_close_notify (GTlsConnection *conn, + gboolean require_close_notify); +gboolean g_tls_connection_get_require_close_notify (GTlsConnection *conn); + +void g_tls_connection_set_rehandshake_mode (GTlsConnection *conn, + GTlsRehandshakeMode mode); +GTlsRehandshakeMode g_tls_connection_get_rehandshake_mode (GTlsConnection *conn); + +gboolean g_tls_connection_handshake (GTlsConnection *conn, + GCancellable *cancellable, + GError **error); + +void g_tls_connection_handshake_async (GTlsConnection *conn, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_tls_connection_handshake_finish (GTlsConnection *conn, + GAsyncResult *result, + GError **error); + +/** + * G_TLS_ERROR: + * + * Error domain for TLS. Errors in this domain will be from the + * #GTlsError enumeration. See #GError for more information on error + * domains. + */ +#define G_TLS_ERROR (g_tls_error_quark ()) +GQuark g_tls_error_quark (void); + + +/*< protected >*/ +GTlsCertificate *g_tls_connection_emit_need_certificate (GTlsConnection *conn); +gboolean g_tls_connection_emit_accept_certificate (GTlsConnection *conn, + GTlsCertificate *peer_cert, + GTlsCertificateFlags errors); + +void g_tls_connection_set_peer_certificate (GTlsConnection *conn, + GTlsCertificate *certificate); + +G_END_DECLS + +#endif /* __G_TLS_CONNECTION_H__ */ diff --git a/gio/gtlsserverconnection.c b/gio/gtlsserverconnection.c new file mode 100644 index 000000000..791a0dd7b --- /dev/null +++ b/gio/gtlsserverconnection.c @@ -0,0 +1,96 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2010 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "glib.h" + +#include "gtlsserverconnection.h" +#include "ginitable.h" +#include "gio-marshal.h" +#include "gioenumtypes.h" +#include "gsocket.h" +#include "gtlsbackend.h" +#include "gtlscertificate.h" +#include "glibintl.h" + +/** + * SECTION:gtlsserverconnection + * @short_description: TLS server-side connection + * @include: gio/gio.h + * + * #GTlsServerConnection is the server-side subclass of #GTlsConnection, + * representing a server-side TLS connection. + * + * Since: 2.28 + */ + +G_DEFINE_INTERFACE (GTlsServerConnection, g_tls_server_connection, G_TYPE_TLS_CONNECTION) + +static void +g_tls_server_connection_default_init (GTlsServerConnectionInterface *iface) +{ + /** + * GTlsServerConnection:authentication-mode: + * + * The #GTlsAuthenticationMode for the server. This can be changed + * before calling g_tls_connection_handshake() if you want to + * rehandshake with a different mode from the initial handshake. + * + * Since: 2.28 + */ + g_object_interface_install_property (iface, + g_param_spec_enum ("authentication-mode", + P_("Authentication Mode"), + P_("The client authentication mode"), + G_TYPE_TLS_AUTHENTICATION_MODE, + G_TLS_AUTHENTICATION_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +/** + * g_tls_server_connection_new: + * @base_io_stream: the #GIOStream to wrap + * @certificate: (allow-none): the default server certificate, or %NULL + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a new #GTlsServerConnection wrapping @base_io_stream (which + * must have pollable input and output streams). + * + * Return value: the new #GTlsServerConnection, or %NULL on error + * + * Since: 2.28 + */ +GTlsServerConnection * +g_tls_server_connection_new (GIOStream *base_io_stream, + GTlsCertificate *certificate, + GError **error) +{ + GObject *conn; + GTlsBackend *backend; + + backend = g_tls_backend_get_default (); + conn = g_initable_new (g_tls_backend_get_server_connection_type (backend), + NULL, error, + "base-io-stream", base_io_stream, + "certificate", certificate, + NULL); + return G_TLS_SERVER_CONNECTION (conn); +} diff --git a/gio/gtlsserverconnection.h b/gio/gtlsserverconnection.h new file mode 100644 index 000000000..976996eff --- /dev/null +++ b/gio/gtlsserverconnection.h @@ -0,0 +1,61 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __G_TLS_SERVER_CONNECTION_H__ +#define __G_TLS_SERVER_CONNECTION_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_TLS_SERVER_CONNECTION (g_tls_server_connection_get_type ()) +#define G_TLS_SERVER_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_SERVER_CONNECTION, GTlsServerConnection)) +#define G_IS_TLS_SERVER_CONNECTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_SERVER_CONNECTION)) +#define G_TLS_SERVER_CONNECTION_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), G_TYPE_TLS_SERVER_CONNECTION, GTlsServerConnectionInterface)) + +/** + * GTlsServerConnection: + * + * TLS server-side connection. This is the server-side implementation + * of a #GTlsConnection. + * + * Since: 2.28 + */ +typedef struct _GTlsServerConnectionInterface GTlsServerConnectionInterface; + +struct _GTlsServerConnectionInterface +{ + GTypeInterface g_iface; + +}; + +GType g_tls_server_connection_get_type (void) G_GNUC_CONST; + +GTlsServerConnection *g_tls_server_connection_new (GIOStream *base_io_stream, + GTlsCertificate *certificate, + GError **error); + +G_END_DECLS + +#endif /* __G_TLS_SERVER_CONNECTION_H__ */ diff --git a/gio/tests/socket-client.c b/gio/tests/socket-client.c index 1f1455e5a..7c44165a6 100644 --- a/gio/tests/socket-client.c +++ b/gio/tests/socket-client.c @@ -5,17 +5,15 @@ #include #include -#include "socket-common.c" - GMainLoop *loop; gboolean verbose = FALSE; gboolean non_blocking = FALSE; gboolean use_udp = FALSE; -gboolean use_source = FALSE; int cancel_timeout = 0; int read_timeout = 0; gboolean unix_socket = FALSE; +gboolean tls = FALSE; static GOptionEntry cmd_entries[] = { {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, @@ -26,70 +24,39 @@ static GOptionEntry cmd_entries[] = { "Be verbose", NULL}, {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, "Enable non-blocking i/o", NULL}, - {"use-source", 's', 0, G_OPTION_ARG_NONE, &use_source, - "Use GSource to wait for non-blocking i/o", NULL}, #ifdef G_OS_UNIX {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket, "Use a unix socket instead of IP", NULL}, #endif {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout, "Time out reads after the specified number of seconds", NULL}, + {"tls", 'T', 0, G_OPTION_ARG_NONE, &tls, + "Use TLS (SSL)", NULL}, {NULL} }; +#include "socket-common.c" + static gboolean -source_ready (gpointer data, - GIOCondition condition) +accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert, + GTlsCertificateFlags errors, gpointer user_data) { - g_main_loop_quit (loop); - return FALSE; -} + g_print ("Certificate would have been rejected ( "); + if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA) + g_print ("unknown-ca "); + if (errors & G_TLS_CERTIFICATE_BAD_IDENTITY) + g_print ("bad-identity "); + if (errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) + g_print ("not-activated "); + if (errors & G_TLS_CERTIFICATE_EXPIRED) + g_print ("expired "); + if (errors & G_TLS_CERTIFICATE_REVOKED) + g_print ("revoked "); + if (errors & G_TLS_CERTIFICATE_INSECURE) + g_print ("insecure "); + g_print (") but accepting anyway.\n"); -static void -ensure_condition (GSocket *socket, - const char *where, - GCancellable *cancellable, - GIOCondition condition) -{ - GError *error = NULL; - GSource *source; - - if (!non_blocking) - return; - - if (use_source) - { - source = g_socket_create_source (socket, - condition, - cancellable); - g_source_set_callback (source, - (GSourceFunc) source_ready, - NULL, NULL); - g_source_attach (source, NULL); - g_source_unref (source); - g_main_loop_run (loop); - } - else - { - if (!g_socket_condition_wait (socket, condition, cancellable, &error)) - { - g_printerr ("condition wait error for %s: %s\n", - where, - error->message); - exit (1); - } - } -} - -static gpointer -cancel_thread (gpointer data) -{ - GCancellable *cancellable = data; - - g_usleep (1000*1000*cancel_timeout); - g_print ("Cancelling\n"); - g_cancellable_cancel (cancellable); - return NULL; + return TRUE; } int @@ -106,6 +73,9 @@ main (int argc, GCancellable *cancellable; GSocketAddressEnumerator *enumerator; GSocketConnectable *connectable; + GIOStream *connection; + GInputStream *istream; + GOutputStream *ostream; g_thread_init (NULL); @@ -125,6 +95,12 @@ main (int argc, return 1; } + if (use_udp && tls) + { + g_printerr ("DTLS (TLS over UDP) is not supported"); + return 1; + } + if (cancel_timeout) { cancellable = g_cancellable_new (); @@ -201,15 +177,10 @@ main (int argc, g_object_unref (address); } g_object_unref (enumerator); - g_object_unref (connectable); g_print ("Connected to %s\n", socket_address_to_string (address)); - /* TODO: Test non-blocking connect */ - if (non_blocking) - g_socket_set_blocking (socket, FALSE); - src_address = g_socket_get_local_address (socket, &error); if (!src_address) { @@ -221,6 +192,49 @@ main (int argc, socket_address_to_string (src_address)); g_object_unref (src_address); + if (use_udp) + connection = NULL; + else + connection = G_IO_STREAM (g_socket_connection_factory_create_connection (socket)); + + if (tls) + { + GTlsClientConnection *tls_conn; + + tls_conn = g_tls_client_connection_new (connection, connectable, &error); + if (!tls_conn) + { + g_printerr ("Could not create TLS connection: %s\n", + error->message); + return 1; + } + + g_signal_connect (tls_conn, "accept-certificate", + G_CALLBACK (accept_certificate), NULL); + + if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), + cancellable, &error)) + { + g_printerr ("Error during TLS handshake: %s\n", + error->message); + return 1; + } + + g_object_unref (connection); + connection = G_IO_STREAM (tls_conn); + } + g_object_unref (connectable); + + if (connection) + { + istream = g_io_stream_get_input_stream (connection); + ostream = g_io_stream_get_output_stream (connection); + } + + /* TODO: Test non-blocking connect/handshake */ + if (non_blocking) + g_socket_set_blocking (socket, FALSE); + while (TRUE) { gchar buffer[4096]; @@ -233,14 +247,20 @@ main (int argc, to_send = strlen (buffer); while (to_send > 0) { - ensure_condition (socket, "send", cancellable, G_IO_OUT); if (use_udp) - size = g_socket_send_to (socket, address, - buffer, to_send, - cancellable, &error); + { + ensure_socket_condition (socket, G_IO_OUT, cancellable); + size = g_socket_send_to (socket, address, + buffer, to_send, + cancellable, &error); + } else - size = g_socket_send (socket, buffer, to_send, - cancellable, &error); + { + ensure_connection_condition (connection, G_IO_OUT, cancellable); + size = g_output_stream_write (ostream, + buffer, to_send, + cancellable, &error); + } if (size < 0) { @@ -272,14 +292,20 @@ main (int argc, to_send -= size; } - ensure_condition (socket, "receive", cancellable, G_IO_IN); if (use_udp) - size = g_socket_receive_from (socket, &src_address, + { + ensure_socket_condition (socket, G_IO_IN, cancellable); + size = g_socket_receive_from (socket, &src_address, + buffer, sizeof buffer, + cancellable, &error); + } + else + { + ensure_connection_condition (connection, G_IO_IN, cancellable); + size = g_input_stream_read (istream, buffer, sizeof buffer, cancellable, &error); - else - size = g_socket_receive (socket, buffer, sizeof buffer, - cancellable, &error); + } if (size < 0) { @@ -306,15 +332,28 @@ main (int argc, g_print ("closing socket\n"); - if (!g_socket_close (socket, &error)) + if (connection) { - g_printerr ("Error closing master socket: %s\n", - error->message); - return 1; + if (!g_io_stream_close (connection, cancellable, &error)) + { + g_printerr ("Error closing connection: %s\n", + error->message); + return 1; + } + g_object_unref (connection); + } + else + { + if (!g_socket_close (socket, &error)) + { + g_printerr ("Error closing master socket: %s\n", + error->message); + return 1; + } } - g_object_unref (G_OBJECT (socket)); - g_object_unref (G_OBJECT (address)); + g_object_unref (socket); + g_object_unref (address); return 0; } diff --git a/gio/tests/socket-common.c b/gio/tests/socket-common.c index 8d2933b0a..fdee3830a 100644 --- a/gio/tests/socket-common.c +++ b/gio/tests/socket-common.c @@ -58,3 +58,64 @@ socket_address_from_string (const char *name) #endif return NULL; } + +static gboolean +source_ready (GPollableInputStream *stream, + gpointer data) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +ensure_socket_condition (GSocket *socket, + GIOCondition condition, + GCancellable *cancellable) +{ + GSource *source; + + if (!non_blocking) + return; + + source = g_socket_create_source (socket, condition, cancellable); + g_source_set_callback (source, + (GSourceFunc) source_ready, + NULL, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + g_main_loop_run (loop); +} + +static void +ensure_connection_condition (GIOStream *stream, + GIOCondition condition, + GCancellable *cancellable) +{ + GSource *source; + + if (!non_blocking) + return; + + if (condition & G_IO_IN) + source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (stream)), cancellable); + else + source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (stream)), cancellable); + + g_source_set_callback (source, + (GSourceFunc) source_ready, + NULL, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + g_main_loop_run (loop); +} + +static gpointer +cancel_thread (gpointer data) +{ + GCancellable *cancellable = data; + + g_usleep (1000*1000*cancel_timeout); + g_print ("Cancelling\n"); + g_cancellable_cancel (cancellable); + return NULL; +} diff --git a/gio/tests/socket-server.c b/gio/tests/socket-server.c index 562f1efd7..d48a5a4c0 100644 --- a/gio/tests/socket-server.c +++ b/gio/tests/socket-server.c @@ -4,8 +4,6 @@ #include #include -#include "socket-common.c" - GMainLoop *loop; int port = 7777; @@ -13,11 +11,11 @@ gboolean verbose = FALSE; gboolean dont_reuse_address = FALSE; gboolean non_blocking = FALSE; gboolean use_udp = FALSE; -gboolean use_source = FALSE; int cancel_timeout = 0; int read_timeout = 0; int delay = 0; gboolean unix_socket = FALSE; +const char *tls_cert_file = NULL; static GOptionEntry cmd_entries[] = { {"port", 'p', 0, G_OPTION_ARG_INT, &port, @@ -32,8 +30,6 @@ static GOptionEntry cmd_entries[] = { "Don't SOADDRREUSE", NULL}, {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, "Enable non-blocking i/o", NULL}, - {"use-source", 's', 0, G_OPTION_ARG_NONE, &use_source, - "Use GSource to wait for non-blocking i/o", NULL}, #ifdef G_OS_UNIX {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket, "Use a unix socket instead of IP", NULL}, @@ -42,63 +38,12 @@ static GOptionEntry cmd_entries[] = { "Delay responses by the specified number of seconds", NULL}, {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout, "Time out reads after the specified number of seconds", NULL}, + {"tls", 'T', 0, G_OPTION_ARG_STRING, &tls_cert_file, + "Use TLS (SSL) with indicated server certificate", "CERTFILE"}, {NULL} }; -static gboolean -source_ready (gpointer data, - GIOCondition condition) -{ - g_main_loop_quit (loop); - return FALSE; -} - -static void -ensure_condition (GSocket *socket, - const char *where, - GCancellable *cancellable, - GIOCondition condition) -{ - GError *error = NULL; - GSource *source; - - if (!non_blocking) - return; - - if (use_source) - { - source = g_socket_create_source (socket, - condition, - cancellable); - g_source_set_callback (source, - (GSourceFunc) source_ready, - NULL, NULL); - g_source_attach (source, NULL); - g_source_unref (source); - g_main_loop_run (loop); - } - else - { - if (!g_socket_condition_wait (socket, condition, cancellable, &error)) - { - g_printerr ("condition wait error for %s: %s\n", - where, - error->message); - exit (1); - } - } -} - -static gpointer -cancel_thread (gpointer data) -{ - GCancellable *cancellable = data; - - g_usleep (1000*1000*cancel_timeout); - g_print ("Cancelling\n"); - g_cancellable_cancel (cancellable); - return NULL; -} +#include "socket-common.c" int main (int argc, @@ -113,6 +58,10 @@ main (int argc, GOptionContext *context; GCancellable *cancellable; char *display_addr; + GTlsCertificate *tlscert = NULL; + GIOStream *connection; + GInputStream *istream; + GOutputStream *ostream; g_thread_init (NULL); @@ -142,6 +91,23 @@ main (int argc, cancellable = NULL; } + if (tls_cert_file) + { + if (use_udp) + { + g_printerr ("DTLS (TLS over UDP) is not supported"); + return 1; + } + + tlscert = g_tls_certificate_new_from_file (tls_cert_file, &error); + if (!tlscert) + { + g_printerr ("Could not read server certificate '%s': %s\n", + tls_cert_file, error->message); + return 1; + } + } + loop = g_main_loop_new (NULL, FALSE); if (use_udp) @@ -205,7 +171,7 @@ main (int argc, g_print ("listening on %s...\n", display_addr); g_free (display_addr); - ensure_condition (socket, "accept", cancellable, G_IO_IN); + ensure_socket_condition (socket, G_IO_IN, cancellable); new_socket = g_socket_accept (socket, cancellable, &error); if (!new_socket) { @@ -233,13 +199,45 @@ main (int argc, g_object_unref (address); recv_socket = new_socket; + + connection = G_IO_STREAM (g_socket_connection_factory_create_connection (recv_socket)); + g_object_unref (new_socket); } else { recv_socket = socket; - new_socket = NULL; + connection = NULL; } + if (tlscert) + { + GTlsServerConnection *tls_conn; + + tls_conn = g_tls_server_connection_new (connection, tlscert, &error); + if (!tls_conn) + { + g_printerr ("Could not create TLS connection: %s\n", + error->message); + return 1; + } + + if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), + cancellable, &error)) + { + g_printerr ("Error during TLS handshake: %s\n", + error->message); + return 1; + } + + g_object_unref (connection); + connection = G_IO_STREAM (tls_conn); + } + + if (connection) + { + istream = g_io_stream_get_input_stream (connection); + ostream = g_io_stream_get_output_stream (connection); + } while (TRUE) { @@ -247,14 +245,20 @@ main (int argc, gssize size; gsize to_send; - ensure_condition (recv_socket, "receive", cancellable, G_IO_IN); if (use_udp) - size = g_socket_receive_from (recv_socket, &address, + { + ensure_socket_condition (recv_socket, G_IO_IN, cancellable); + size = g_socket_receive_from (recv_socket, &address, + buffer, sizeof buffer, + cancellable, &error); + } + else + { + ensure_connection_condition (connection, G_IO_IN, cancellable); + size = g_input_stream_read (istream, buffer, sizeof buffer, cancellable, &error); - else - size = g_socket_receive (recv_socket, buffer, sizeof buffer, - cancellable, &error); + } if (size < 0) { @@ -288,13 +292,19 @@ main (int argc, while (to_send > 0) { - ensure_condition (recv_socket, "send", cancellable, G_IO_OUT); if (use_udp) - size = g_socket_send_to (recv_socket, address, - buffer, to_send, cancellable, &error); + { + ensure_socket_condition (recv_socket, G_IO_OUT, cancellable); + size = g_socket_send_to (recv_socket, address, + buffer, to_send, cancellable, &error); + } else - size = g_socket_send (recv_socket, buffer, to_send, - cancellable, &error); + { + ensure_connection_condition (connection, G_IO_OUT, cancellable); + size = g_output_stream_write (ostream, + buffer, to_send, + cancellable, &error); + } if (size < 0) { @@ -329,16 +339,15 @@ main (int argc, g_print ("connection closed\n"); - if (new_socket) + if (connection) { - if (!g_socket_close (new_socket, &error)) + if (!g_io_stream_close (connection, NULL, &error)) { - g_printerr ("Error closing connection socket: %s\n", + g_printerr ("Error closing connection stream: %s\n", error->message); return 1; } - - g_object_unref (G_OBJECT (new_socket)); + g_object_unref (connection); } if (!g_socket_close (socket, &error)) @@ -347,8 +356,7 @@ main (int argc, error->message); return 1; } - - g_object_unref (G_OBJECT (socket)); + g_object_unref (socket); return 0; }