[glib/tls: 7/7] Add TLS (SSL) support to gio
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/tls: 7/7] Add TLS (SSL) support to gio
- Date: Sat, 6 Nov 2010 21:41:13 +0000 (UTC)
commit 245ddf75df2486ddf640572f9340dbab32609712
Author: Dan Winship <danw gnome org>
Date: Mon Dec 21 20:50:32 2009 +0100
Add TLS (SSL) support to gio
This adds support for TLS connections to GSocketConnection, with
an initial implementation using gnutls.
Still a work in progress; the gnutls code needs to migrate to
glib-networking, some APIs need to be added, and some may be changed.
configure.ac | 31 +
docs/reference/gio/gio-docs.xml | 9 +
docs/reference/gio/gio-sections.txt | 132 ++++-
docs/reference/gio/gio.types | 8 +
gio/Makefile.am | 15 +
gio/TLS-NOTES.txt | 37 +
gio/gio-marshal.list | 2 +
gio/gio.h | 4 +
gio/gio.symbols | 80 ++-
gio/gioenums.h | 81 ++
gio/giotypes.h | 8 +
gio/gnutls/Makefile.am | 40 +
gio/gnutls/gtlsbackend-gnutls.c | 196 +++++
gio/gnutls/gtlsbackend-gnutls.h | 49 ++
gio/gnutls/gtlscertificate-gnutls.c | 364 +++++++++
gio/gnutls/gtlscertificate-gnutls.h | 55 ++
gio/gnutls/gtlsclientconnection-gnutls.c | 315 ++++++++
gio/gnutls/gtlsclientconnection-gnutls.h | 47 ++
gio/gnutls/gtlsconnection-gnutls.c | 1186 ++++++++++++++++++++++++++++++
gio/gnutls/gtlsconnection-gnutls.h | 77 ++
gio/gnutls/gtlsinputstream-gnutls.c | 240 ++++++
gio/gnutls/gtlsinputstream-gnutls.h | 48 ++
gio/gnutls/gtlsoutputstream-gnutls.c | 240 ++++++
gio/gnutls/gtlsoutputstream-gnutls.h | 48 ++
gio/gnutls/gtlsserverconnection-gnutls.c | 196 +++++
gio/gnutls/gtlsserverconnection-gnutls.h | 47 ++
gio/gsocketclient.c | 313 +++++++--
gio/gsocketclient.h | 10 +
gio/gtlsbackend.c | 157 ++++
gio/gtlsbackend.h | 76 ++
gio/gtlscertificate.c | 508 +++++++++++++
gio/gtlscertificate.h | 78 ++
gio/gtlsclientconnection.c | 337 +++++++++
gio/gtlsclientconnection.h | 71 ++
gio/gtlsconnection.c | 741 +++++++++++++++++++
gio/gtlsconnection.h | 141 ++++
gio/gtlsserverconnection.c | 105 +++
gio/gtlsserverconnection.h | 60 ++
gio/tests/.gitignore | 1 +
gio/tests/Makefile.am | 9 +-
gio/tests/socket-client.c | 198 +++--
gio/tests/socket-common.c | 61 ++
gio/tests/socket-server.c | 167 +++--
gio/tests/test-cert.pem | 37 +
gio/tests/tls-test.c | 340 +++++++++
45 files changed, 6754 insertions(+), 211 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cd797d7..a4e3b8d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1035,6 +1035,36 @@ case $host in
;;
esac
+dnl temporary
+PKG_CHECK_MODULES(LIBGNUTLS, gnutls >= 2.1.7, [AM_PATH_LIBGCRYPT([])])
+
+AC_MSG_CHECKING([location of system Certificate Authority list])
+AC_ARG_WITH(ca-file,
+ [AC_HELP_STRING([--with-ca-file=@<:@path@:>@],
+ [path to system Certificate Authority list])])
+if test "$with_ca_file" = "no"; then
+ AC_MSG_RESULT([disabled])
+else
+ if test -z "$with_ca_file"; then
+ for f in /etc/pki/tls/certs/ca-bundle.crt \
+ /etc/ssl/certs/ca-certificates.crt; do
+ if test -f "$f"; then
+ with_ca_file="$f"
+ fi
+ done
+ if test -z "$with_ca_file"; then
+ AC_MSG_ERROR([could not find. Use --with-ca-file=path to set, or --without-ca-file to disable])
+ fi
+ fi
+
+ AC_MSG_RESULT($with_ca_file)
+ if ! test -f "$with_ca_file"; then
+ AC_MSG_ERROR([No such file '$with_ca_file'. Use --with-ca-file=path to set, or --without-ca-file to disable])
+ fi
+
+ AC_DEFINE_UNQUOTED(GTLS_SYSTEM_CA_FILE, ["$with_ca_file"], [The system TLS CA list])
+fi
+
dnl
dnl if statfs() takes 2 arguments (Posix) or 4 (Solaris)
dnl
@@ -3736,6 +3766,7 @@ gio/libasyncns/Makefile
gio/fen/Makefile
gio/fam/Makefile
gio/win32/Makefile
+gio/gnutls/Makefile
gio/tests/Makefile
po/Makefile.in
docs/Makefile
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index 4285a65..c137362 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -126,6 +126,15 @@
<xi:include href="xml/gsocketservice.xml"/>
<xi:include href="xml/gthreadedsocketservice.xml"/>
</chapter>
+ <chapter id="tls">
+ <title>TLS (SSL) support</title>
+ <xi:include href="xml/gtls.xml"/>
+ <xi:include href="xml/gtlscertificate.xml"/>
+ <xi:include href="xml/gtlsconnection.xml"/>
+ <xi:include href="xml/gtlsclientconnection.xml"/>
+ <xi:include href="xml/gtlsserverconnection.xml"/>
+ <xi:include href="xml/gtlsbackend.xml"/>
+ </chapter>
<chapter id="resolver">
<title>DNS resolution</title>
<xi:include href="xml/gresolver.xml"/>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 1c76c5b..442c15b 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1795,13 +1795,19 @@ 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_handshake
+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_handshake
+g_socket_client_get_tls_validation_flags
<SUBSECTION Standard>
GSocketClientClass
G_IS_SOCKET_CLIENT
@@ -3004,3 +3010,127 @@ G_TYPE_POLLABLE_OUTPUT_STREAM
<SUBSECTION Private>
g_pollable_output_stream_get_type
</SECTION>
+
+<SECTION>
+<FILE>gtls</FILE>
+G_TLS_ERROR
+GTlsError
+<SUBSECTION>
+GTlsAuthenticationMode
+GTlsValidationFlags
+</SECTION>
+
+<SECTION>
+<FILE>gtlsbackend</FILE>
+<TITLE>GTlsBackend</FILE>
+G_TLS_BACKEND_EXTENSION_POINT_NAME
+GTlsBackend
+GTlsBackendInterface
+g_tls_backend_get_default
+g_tls_backend_get_certificate_type
+g_tls_backend_get_client_connection_type
+g_tls_backend_get_client_context_type
+g_tls_backend_get_server_connection_type
+g_tls_backend_get_server_context_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</TITLE>
+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
+<SUBSECTION Standard>
+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
+<SUBSECTION Private>
+g_tls_certificate_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlsconnection</FILE>
+<TITLE>GTlsConnection</TITLE>
+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
+g_tls_connection_get_ca_list
+g_tls_connection_set_ca_list
+g_tls_connection_set_ca_list_from_file
+<SUBSECTION>
+g_tls_connection_handshake
+g_tls_connection_handshake_async
+g_tls_connection_handshake_finish
+<SUBSECTION>
+g_tls_connection_set_peer_certificate
+g_tls_connection_emit_accept_certificate
+g_tls_connection_emit_need_certificate
+<SUBSECTION Standard>
+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
+<SUBSECTION Private>
+g_tls_connection_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlsclientconnection</FILE>
+<TITLE>GTlsClientConnection</TITLE>
+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
+<SUBSECTION Standard>
+G_IS_TLS_CLIENT_CONNECTION
+G_TLS_CLIENT_CONNECTION
+G_TLS_CLIENT_CONNECTION_GET_INTERFACE
+G_TYPE_TLS_CLIENT_CONNECTION
+<SUBSECTION Private>
+g_tls_client_connection_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlsserverconnection</FILE>
+<TITLE>GTlsServerConnection</TITLE>
+GTlsServerConnection
+GTlsServerConnectionInterface
+g_tls_server_connection_new
+<SUBSECTION Standard>
+G_IS_TLS_SERVER_CONNECTION
+G_TLS_SERVER_CONNECTION
+G_TLS_SERVER_CONNECTION_GET_INTERFACE
+G_TYPE_TLS_SERVER_CONNECTION
+<SUBSECTION Private>
+g_tls_server_connection_get_type
+</SECTION>
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types
index b047219..89c666e 100644
--- a/docs/reference/gio/gio.types
+++ b/docs/reference/gio/gio.types
@@ -107,6 +107,14 @@ g_srv_target_get_type
g_tcp_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_client_context_get_type
+g_tls_connection_get_type
+g_tls_context_get_type
+g_tls_server_connection_get_type
+g_tls_server_context_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 06c444f..e513d24 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -202,6 +202,11 @@ platform_libadd += win32/libgiowin32.la
platform_deps += win32/libgiowin32.la
endif
+#if HAVE_GNUTLS
+SUBDIRS += gnutls
+platform_libadd += gnutls/libgtlsgnutls.la
+platform_deps += gnutls/libgtlsgnutls.la
+#endif
SUBDIRS += .
@@ -382,6 +387,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 \
@@ -532,6 +542,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/TLS-NOTES.txt b/gio/TLS-NOTES.txt
new file mode 100644
index 0000000..9923c35
--- /dev/null
+++ b/gio/TLS-NOTES.txt
@@ -0,0 +1,37 @@
+CRLs?
+ - get/set on connection
+
+pkcs11? tokens, etc
+ - GTlsCertificateDatabase, g_tls_certificate_database_get_default()
+ - looking up a cert may invoke password callback
+ - automatically does looking in a thread so callback can block it?
+ - lookup by DN/nick?/other?
+ - get list of available certs
+
+sessions?
+ - NSS handles this transparently on the client side; could make
+ the gnutls backend do this too, via a global (mutexed) sessiondb
+
+misc options
+ - ciphersuites
+ - compression options?
+ - list/enable/disable TLS extensions?
+
+stupid server SSL 3.0 fallback support
+ - via GSocketClient, or higher level?
+ - if this is really only a problem for HTTP then just punt to libsoup
+
+XMPP forbids renegotiation
+ - could be handled by having an about-to-handshake signal, and
+ the handler could kill the connection from there
+
+need a FIPS140 violation error code
+
+"handshaked" signal?
+ autohandshake in a thread so callback can block in signals?
+
+how will the dynamic types work with bindings?
+
+GTlsConnection:validation-errors
+
+g_socket_client_connect_to_uri(): does TLS? when?
diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list
index 8f85999..97ab078 100644
--- a/gio/gio-marshal.list
+++ b/gio/gio-marshal.list
@@ -25,3 +25,5 @@ VOID:POINTER,INT,STRING
BOOLEAN:OBJECT
INT:OBJECT
VOID:UINT64
+BOOLEAN:OBJECT,FLAGS
+OBJECT:VOID
diff --git a/gio/gio.h b/gio/gio.h
index 6f650df..33d9279 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -121,6 +121,10 @@
#include <gio/gtcpwrapperconnection.h>
#include <gio/gthemedicon.h>
#include <gio/gthreadedsocketservice.h>
+#include <gio/gtlscertificate.h>
+#include <gio/gtlsclientconnection.h>
+#include <gio/gtlsconnection.h>
+#include <gio/gtlsserverconnection.h>
#include <gio/gvfs.h>
#include <gio/gvolume.h>
#include <gio/gvolumemonitor.h>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index f35b3e0..85b9ad8 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1063,6 +1063,8 @@ 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_validation_flags_get_type G_GNUC_CONST
#endif
#endif
@@ -1367,6 +1369,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
@@ -1379,20 +1382,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_context
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_context
#endif
#endif
@@ -2002,3 +2008,71 @@ 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_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_gnutls_copy_cert
+g_tls_certificate_gnutls_copy_key
+g_tls_certificate_gnutls_new
+g_tls_certificate_list_new_from_file
+g_tls_certificate_new
+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_ca_list
+g_tls_connection_get_certificate
+g_tls_connection_get_peer_certificate
+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_ca_list
+g_tls_connection_set_ca_list_from_file
+g_tls_connection_set_certificate
+g_tls_connection_set_peer_certificate
+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 121f54c..52b4983 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1245,6 +1245,87 @@ 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;
+
+/**
+ * GTlsValidationFlags:
+ * @G_TLS_VALIDATE_GENERIC_ERROR: Generic error
+ * @G_TLS_VALIDATE_CA: The signing certificate authority is
+ * not known.
+ * @G_TLS_VALIDATE_IDENTITY: The certificate does not match the
+ * expected identity of the site that it was retrieved from.
+ * @G_TLS_VALIDATE_ACTIVATION: The certificate's activation time
+ * is still in the future
+ * @G_TLS_VALIDATE_EXPIRATION: The certificate has expired
+ * @G_TLS_VALIDATE_REVOCATION: The certificate has been revoked
+ * according to the #GTlsContext's certificate revocation list.
+ * @G_TLS_VALIDATE_ALGORITHM: The certificate's algorithm is
+ * considered insecure.
+ * @G_TLS_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_VALIDATE_GENERIC_ERROR = (2 << 0),
+ G_TLS_VALIDATE_CA = (2 << 1),
+ G_TLS_VALIDATE_IDENTITY = (2 << 2),
+ G_TLS_VALIDATE_ACTIVATION = (2 << 3),
+ G_TLS_VALIDATE_EXPIRATION = (2 << 4),
+ G_TLS_VALIDATE_REVOCATION = (2 << 5),
+ G_TLS_VALIDATE_ALGORITHM = (2 << 6),
+
+ G_TLS_VALIDATE_ALL = 0x003f
+} GTlsValidationFlags;
+
+/**
+ * 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;
+
G_END_DECLS
#endif /* __GIO_ENUMS_H__ */
diff --git a/gio/giotypes.h b/gio/giotypes.h
index cb48a12..178b72b 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;
@@ -203,6 +204,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/gnutls/Makefile.am b/gio/gnutls/Makefile.am
new file mode 100644
index 0000000..1ef3f3f
--- /dev/null
+++ b/gio/gnutls/Makefile.am
@@ -0,0 +1,40 @@
+include $(top_srcdir)/Makefile.decl
+
+NULL =
+
+noinst_LTLIBRARIES = libgtlsgnutls.la
+
+libgtlsgnutls_la_SOURCES = \
+ gtlsbackend-gnutls.c \
+ gtlsbackend-gnutls.h \
+ gtlscertificate-gnutls.c \
+ gtlscertificate-gnutls.h \
+ gtlsclientconnection-gnutls.c \
+ gtlsclientconnection-gnutls.h \
+ gtlsconnection-gnutls.c \
+ gtlsconnection-gnutls.h \
+ gtlsinputstream-gnutls.c \
+ gtlsinputstream-gnutls.h \
+ gtlsoutputstream-gnutls.c \
+ gtlsoutputstream-gnutls.h \
+ gtlsserverconnection-gnutls.c \
+ gtlsserverconnection-gnutls.h \
+ $(NULL)
+
+libgtlsgnutls_la_CFLAGS = \
+ $(LIBGNUTLS_CFLAGS) \
+ $(LIBGCRYPT_CFLAGS) \
+ -DG_LOG_DOMAIN=\"GLib-GIO\" \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/glib \
+ -I$(top_srcdir)/gmodule \
+ -I$(top_srcdir)/gio \
+ -I$(top_builddir)/gio \
+ $(GLIB_DEBUG_FLAGS) \
+ -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \
+ -DGIO_COMPILATION \
+ -DG_DISABLE_DEPRECATED
+
+libgtlsgnutls_la_LIBADD = \
+ $(LIBGNUTLS_LIBS) \
+ $(LIBGCRYPT_LIBS)
diff --git a/gio/gnutls/gtlsbackend-gnutls.c b/gio/gnutls/gtlsbackend-gnutls.c
new file mode 100644
index 0000000..ce310e3
--- /dev/null
+++ b/gio/gnutls/gtlsbackend-gnutls.c
@@ -0,0 +1,196 @@
+/* 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 <errno.h>
+
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+#ifndef G_OS_WIN32
+#include <pthread.h>
+#endif
+
+#include "gtlsbackend-gnutls.h"
+#include "gtlscertificate-gnutls.h"
+#include "gtlsclientconnection-gnutls.h"
+#include "gtlsserverconnection-gnutls.h"
+
+static void gtls_gnutls_init (void);
+static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
+
+#define g_tls_backend_gnutls_get_type _g_tls_backend_gnutls_get_type
+G_DEFINE_TYPE_WITH_CODE (GTlsBackendGnutls, g_tls_backend_gnutls, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_BACKEND,
+ g_tls_backend_gnutls_interface_init);
+ gtls_gnutls_init ();)
+
+#if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+#ifdef G_OS_WIN32
+
+static int
+gtls_gcry_win32_mutex_init (void **priv)
+{
+ int err = 0;
+ CRITICAL_SECTION *lock = (CRITICAL_SECTION*)malloc (sizeof (CRITICAL_SECTION));
+
+ if (!lock)
+ err = ENOMEM;
+ if (!err) {
+ InitializeCriticalSection (lock);
+ *priv = lock;
+ }
+ return err;
+}
+
+static int
+gtls_gcry_win32_mutex_destroy (void **lock)
+{
+ DeleteCriticalSection ((CRITICAL_SECTION*)*lock);
+ free (*lock);
+ return 0;
+}
+
+static int
+gtls_gcry_win32_mutex_lock (void **lock)
+{
+ EnterCriticalSection ((CRITICAL_SECTION*)*lock);
+ return 0;
+}
+
+static int
+gtls_gcry_win32_mutex_unlock (void **lock)
+{
+ LeaveCriticalSection ((CRITICAL_SECTION*)*lock);
+ return 0;
+}
+
+
+static struct gcry_thread_cbs gtls_gcry_threads_win32 = { \
+ (GCRY_THREAD_OPTION_USER | (GCRY_THREAD_OPTION_VERSION << 8)), \
+ NULL, gtls_gcry_win32_mutex_init, gtls_gcry_win32_mutex_destroy, \
+ gtls_gcry_win32_mutex_lock, gtls_gcry_win32_mutex_unlock, \
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+#endif
+
+static void
+gtls_gnutls_init (void)
+{
+#if defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) && !defined(G_OS_WIN32)
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+#elif defined(G_OS_WIN32)
+ gcry_control (GCRYCTL_SET_THREAD_CBS, >ls_gcry_threads_win32);
+#endif
+ gnutls_global_init ();
+}
+
+static void
+g_tls_backend_gnutls_init (GTlsBackendGnutls *backend)
+{
+}
+
+static void
+g_tls_backend_gnutls_class_init (GTlsBackendGnutlsClass *backend_class)
+{
+}
+
+static void
+g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
+{
+ iface->get_certificate_type = _g_tls_certificate_gnutls_get_type;
+ iface->get_client_connection_type = _g_tls_client_connection_gnutls_get_type;
+ iface->get_server_connection_type = _g_tls_server_connection_gnutls_get_type;
+}
+
+#ifdef GTLS_SYSTEM_CA_FILE
+/* Parsing the system CA list takes a noticeable amount of time.
+ * So we only do it once, and only when we actually need to see it.
+ */
+static const GList *
+get_ca_lists (gnutls_x509_crt_t **cas,
+ int *num_cas)
+{
+ static gnutls_x509_crt_t *ca_list_gnutls;
+ static int ca_list_length;
+ static GList *ca_list;
+
+ if (g_once_init_enter ((volatile gsize *)&ca_list_gnutls))
+ {
+ GError *error = NULL;
+ gnutls_x509_crt_t *x509_crts;
+ GList *c;
+ int i;
+
+ ca_list = g_tls_certificate_list_new_from_file (GTLS_SYSTEM_CA_FILE, &error);
+ if (error)
+ {
+ g_warning ("Failed to read system CA file %s: %s.",
+ GTLS_SYSTEM_CA_FILE, error->message);
+ g_error_free (error);
+ /* Note that this is not a security problem, since if
+ * G_TLS_VALIDATE_CA is set, then this just means validation
+ * will always fail, and if it isn't set, then it doesn't
+ * matter that we couldn't read the CAs.
+ */
+ }
+
+ ca_list_length = g_list_length (ca_list);
+ x509_crts = g_new (gnutls_x509_crt_t, ca_list_length);
+ for (c = ca_list, i = 0; c; c = c->next, i++)
+ x509_crts[i] = g_tls_certificate_gnutls_get_cert (c->data);
+
+ g_once_init_leave ((volatile gsize *)&ca_list_gnutls, GPOINTER_TO_SIZE (x509_crts));
+ }
+
+ if (cas)
+ *cas = ca_list_gnutls;
+ if (num_cas)
+ *num_cas = ca_list_length;
+
+ return ca_list;
+}
+#endif
+
+const GList *
+g_tls_backend_gnutls_get_system_ca_list_gtls (void)
+{
+#ifdef GTLS_SYSTEM_CA_FILE
+ return get_ca_lists (NULL, NULL);
+#else
+ return NULL;
+#endif
+}
+
+void
+g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
+ int *num_cas)
+{
+#ifdef GTLS_SYSTEM_CA_FILE
+ get_ca_lists (cas, num_cas);
+#else
+ *cas = NULL;
+ *num_cas = 0;
+#endif
+}
diff --git a/gio/gnutls/gtlsbackend-gnutls.h b/gio/gnutls/gtlsbackend-gnutls.h
new file mode 100644
index 0000000..597fdf2
--- /dev/null
+++ b/gio/gnutls/gtlsbackend-gnutls.h
@@ -0,0 +1,49 @@
+/* GIO - GLib Backend, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_BACKEND_GNUTLS_H__
+#define __G_TLS_BACKEND_GNUTLS_H__
+
+#include <gio/gtlsbackend.h>
+#include <gnutls/gnutls.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_BACKEND_GNUTLS (_g_tls_backend_gnutls_get_type ())
+#define G_TLS_BACKEND_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutls))
+#define G_TLS_BACKEND_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsClass))
+#define G_IS_TLS_BACKEND_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_BACKEND_GNUTLS))
+#define G_IS_TLS_BACKEND_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_BACKEND_GNUTLS))
+#define G_TLS_BACKEND_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_BACKEND_GNUTLS, GTlsBackendGnutlsClass))
+
+typedef struct _GTlsBackendGnutlsClass GTlsBackendGnutlsClass;
+typedef struct _GTlsBackendGnutls GTlsBackendGnutls;
+
+struct _GTlsBackendGnutlsClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GTlsBackendGnutls
+{
+ GObject parent_instance;
+};
+
+GType _g_tls_backend_gnutls_get_type (void) G_GNUC_CONST;
+
+const GList *g_tls_backend_gnutls_get_system_ca_list_gtls (void) G_GNUC_CONST;
+void g_tls_backend_gnutls_get_system_ca_list_gnutls (gnutls_x509_crt_t **cas,
+ int *num_cas);
+
+G_END_DECLS
+
+#endif /* __G_TLS_BACKEND_GNUTLS_H___ */
diff --git a/gio/gnutls/gtlscertificate-gnutls.c b/gio/gnutls/gtlscertificate-gnutls.c
new file mode 100644
index 0000000..4ba3ba8
--- /dev/null
+++ b/gio/gnutls/gtlscertificate-gnutls.c
@@ -0,0 +1,364 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2009 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 <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <string.h>
+
+#include "gtlscertificate-gnutls.h"
+#include "ginitable.h"
+#include "gtlsconnection.h"
+#include "glibintl.h"
+
+static void g_tls_certificate_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void g_tls_certificate_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void g_tls_certificate_gnutls_finalize (GObject *object);
+
+static void g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface);
+static gboolean g_tls_certificate_gnutls_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error);
+
+#define g_tls_certificate_gnutls_get_type _g_tls_certificate_gnutls_get_type
+G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_certificate_gnutls_initable_iface_init);)
+
+enum
+{
+ PROP_0,
+
+ PROP_CERTIFICATE,
+ PROP_CERTIFICATE_PEM,
+ PROP_PRIVATE_KEY,
+ PROP_PRIVATE_KEY_PEM
+};
+
+struct _GTlsCertificateGnutlsPrivate
+{
+ gnutls_x509_crt_t cert;
+ gnutls_x509_privkey_t key;
+
+ GError *construct_error;
+
+ guint have_cert : 1;
+ guint have_key : 1;
+};
+
+static void
+g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsCertificateGnutlsPrivate));
+
+ gobject_class->get_property = g_tls_certificate_gnutls_get_property;
+ gobject_class->set_property = g_tls_certificate_gnutls_set_property;
+ gobject_class->finalize = g_tls_certificate_gnutls_finalize;
+
+ 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_tls_certificate_gnutls_finalize (GObject *object)
+{
+ GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
+
+ gnutls_x509_crt_deinit (gnutls->priv->cert);
+ gnutls_x509_privkey_deinit (gnutls->priv->key);
+
+ g_clear_error (&gnutls->priv->construct_error);
+
+ G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_certificate_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
+ GByteArray *certificate;
+ char *certificate_pem;
+ int status;
+ size_t size;
+
+ switch (prop_id)
+ {
+ case PROP_CERTIFICATE:
+ size = 0;
+ status = gnutls_x509_crt_export (gnutls->priv->cert,
+ GNUTLS_X509_FMT_DER,
+ NULL, &size);
+ if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ certificate = NULL;
+ else
+ {
+ certificate = g_byte_array_sized_new (size);
+ certificate->len = size;
+ status = gnutls_x509_crt_export (gnutls->priv->cert,
+ GNUTLS_X509_FMT_DER,
+ certificate->data, &size);
+ if (status != 0)
+ {
+ g_byte_array_free (certificate, TRUE);
+ certificate = NULL;
+ }
+ }
+ g_value_take_boxed (value, certificate);
+ break;
+
+ case PROP_CERTIFICATE_PEM:
+ size = 0;
+ status = gnutls_x509_crt_export (gnutls->priv->cert,
+ GNUTLS_X509_FMT_PEM,
+ NULL, &size);
+ if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ certificate_pem = NULL;
+ else
+ {
+ certificate_pem = g_malloc (size);
+ status = gnutls_x509_crt_export (gnutls->priv->cert,
+ GNUTLS_X509_FMT_PEM,
+ certificate_pem, &size);
+ if (status != 0)
+ {
+ g_free (certificate_pem);
+ certificate_pem = NULL;
+ }
+ }
+ g_value_take_string (value, certificate_pem);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_certificate_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
+ GByteArray *bytes;
+ const char *string;
+ gnutls_datum_t data;
+ int status;
+
+ switch (prop_id)
+ {
+ case PROP_CERTIFICATE:
+ bytes = g_value_get_boxed (value);
+ if (!bytes)
+ break;
+ g_return_if_fail (gnutls->priv->have_cert == FALSE);
+ data.data = bytes->data;
+ data.size = bytes->len;
+ status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
+ GNUTLS_X509_FMT_DER);
+ if (status == 0)
+ gnutls->priv->have_cert = TRUE;
+ else if (!gnutls->priv->construct_error)
+ {
+ gnutls->priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse DER certificate: %s"),
+ gnutls_strerror (status));
+ }
+
+ break;
+
+ case PROP_CERTIFICATE_PEM:
+ string = g_value_get_string (value);
+ if (!string)
+ break;
+ g_return_if_fail (gnutls->priv->have_cert == FALSE);
+ data.data = (void *)string;
+ data.size = strlen (string);
+ status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
+ GNUTLS_X509_FMT_PEM);
+ if (status == 0)
+ gnutls->priv->have_cert = TRUE;
+ else if (!gnutls->priv->construct_error)
+ {
+ gnutls->priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse PEM certificate: %s"),
+ gnutls_strerror (status));
+ }
+ break;
+
+ case PROP_PRIVATE_KEY:
+ bytes = g_value_get_boxed (value);
+ if (!bytes)
+ break;
+ g_return_if_fail (gnutls->priv->have_key == FALSE);
+ data.data = bytes->data;
+ data.size = bytes->len;
+ status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
+ GNUTLS_X509_FMT_DER);
+ if (status == 0)
+ gnutls->priv->have_key = TRUE;
+ else if (!gnutls->priv->construct_error)
+ {
+ gnutls->priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse DER private key: %s"),
+ gnutls_strerror (status));
+ }
+ break;
+
+ case PROP_PRIVATE_KEY_PEM:
+ string = g_value_get_string (value);
+ if (!string)
+ break;
+ g_return_if_fail (gnutls->priv->have_key == FALSE);
+ data.data = (void *)string;
+ data.size = strlen (string);
+ status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
+ GNUTLS_X509_FMT_PEM);
+ if (status == 0)
+ gnutls->priv->have_key = TRUE;
+ else if (!gnutls->priv->construct_error)
+ {
+ gnutls->priv->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not parse PEM private key: %s"),
+ gnutls_strerror (status));
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
+{
+ gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls,
+ G_TYPE_TLS_CERTIFICATE_GNUTLS,
+ GTlsCertificateGnutlsPrivate);
+
+ gnutls_x509_crt_init (&gnutls->priv->cert);
+ gnutls_x509_privkey_init (&gnutls->priv->key);
+}
+
+static void
+g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_certificate_gnutls_initable_init;
+}
+
+static gboolean
+g_tls_certificate_gnutls_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (initable);
+
+ if (gnutls->priv->construct_error)
+ {
+ g_propagate_error (error, gnutls->priv->construct_error);
+ gnutls->priv->construct_error = NULL;
+ return FALSE;
+ }
+ else if (!gnutls->priv->have_cert)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("No certificate data provided"));
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+GTlsCertificate *
+g_tls_certificate_gnutls_new (const gnutls_datum *datum,
+ GTlsCertificate *issuer)
+{
+ GTlsCertificateGnutls *gnutls;
+
+ gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
+ "issuer", issuer,
+ NULL);
+ if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
+ GNUTLS_X509_FMT_DER) == 0)
+ gnutls->priv->have_cert = TRUE;
+
+ return G_TLS_CERTIFICATE (gnutls);
+}
+
+const gnutls_x509_crt_t
+g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
+{
+ return gnutls->priv->cert;
+}
+
+const gnutls_x509_privkey_t
+g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls)
+{
+ return gnutls->priv->key;
+}
+
+gnutls_x509_crt_t
+g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls)
+{
+ gnutls_x509_crt_t cert;
+ gnutls_datum data;
+
+ data.size = 0;
+ gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
+ NULL, &data.size);
+ data.data = g_malloc (data.size);
+ gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
+ data.data, &data.size);
+
+ gnutls_x509_crt_init (&cert);
+ gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
+ g_free (data.data);
+
+ return cert;
+}
+
+gnutls_x509_privkey_t
+g_tls_certificate_gnutls_copy_key (GTlsCertificateGnutls *gnutls)
+{
+ gnutls_x509_privkey_t key;
+
+ gnutls_x509_privkey_init (&key);
+ gnutls_x509_privkey_cpy (key, gnutls->priv->key);
+ return key;
+}
diff --git a/gio/gnutls/gtlscertificate-gnutls.h b/gio/gnutls/gtlscertificate-gnutls.h
new file mode 100644
index 0000000..ba5758b
--- /dev/null
+++ b/gio/gnutls/gtlscertificate-gnutls.h
@@ -0,0 +1,55 @@
+/* GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright © 2009 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_CERTIFICATE_GNUTLS_H__
+#define __G_TLS_CERTIFICATE_GNUTLS_H__
+
+#include <gio/gtlscertificate.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CERTIFICATE_GNUTLS (_g_tls_certificate_gnutls_get_type ())
+#define G_TLS_CERTIFICATE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS, GTlsCertificateGnutls))
+#define G_TLS_CERTIFICATE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CERTIFICATE_GNUTLS, GTlsCertificateGnutlsClass))
+#define G_IS_TLS_CERTIFICATE_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS))
+#define G_IS_TLS_CERTIFICATE_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CERTIFICATE_GNUTLS))
+#define G_TLS_CERTIFICATE_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CERTIFICATE_GNUTLS, GTlsCertificateGnutlsClass))
+
+typedef struct _GTlsCertificateGnutlsPrivate GTlsCertificateGnutlsPrivate;
+typedef struct _GTlsCertificateGnutlsClass GTlsCertificateGnutlsClass;
+typedef struct _GTlsCertificateGnutls GTlsCertificateGnutls;
+
+struct _GTlsCertificateGnutlsClass
+{
+ GTlsCertificateClass parent_class;
+};
+
+struct _GTlsCertificateGnutls
+{
+ GTlsCertificate parent_instance;
+ GTlsCertificateGnutlsPrivate *priv;
+};
+
+GType _g_tls_certificate_gnutls_get_type (void) G_GNUC_CONST;
+
+GTlsCertificate * g_tls_certificate_gnutls_new (const gnutls_datum *datum,
+ GTlsCertificate *issuer);
+
+const gnutls_x509_crt_t g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls);
+const gnutls_x509_privkey_t g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls);
+
+gnutls_x509_crt_t g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls);
+gnutls_x509_privkey_t g_tls_certificate_gnutls_copy_key (GTlsCertificateGnutls *gnutls);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CERTIFICATE_GNUTLS_H___ */
diff --git a/gio/gnutls/gtlsclientconnection-gnutls.c b/gio/gnutls/gtlsclientconnection-gnutls.c
new file mode 100644
index 0000000..e84206f
--- /dev/null
+++ b/gio/gnutls/gtlsclientconnection-gnutls.c
@@ -0,0 +1,315 @@
+/* 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 <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <string.h>
+
+#include "gtlsclientconnection-gnutls.h"
+#include "ginitable.h"
+#include "gioenumtypes.h"
+#include "gnetworkaddress.h"
+#include "gsocket.h"
+#include "glibintl.h"
+
+enum
+{
+ PROP_0,
+ PROP_VALIDATION_FLAGS,
+ PROP_SERVER_IDENTITY,
+ PROP_USE_SSL3,
+ PROP_ACCEPTED_CAS
+};
+
+static void g_tls_client_connection_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void g_tls_client_connection_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void g_tls_client_connection_gnutls_finalize (GObject *object);
+
+static void g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn);
+static gboolean g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
+ gboolean success,
+ GError **inout_error);
+
+static void g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface *iface);
+
+static int g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t session,
+ const gnutls_datum_t *req_ca_rdn,
+ int nreqs,
+ const gnutls_pk_algorithm_t *pk_algos,
+ int pk_algos_length,
+ gnutls_retr_st *st);
+
+#define g_tls_client_connection_gnutls_get_type _g_tls_client_connection_gnutls_get_type
+G_DEFINE_TYPE_WITH_CODE (GTlsClientConnectionGnutls, g_tls_client_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION,
+ g_tls_client_connection_gnutls_client_connection_interface_init));
+
+struct _GTlsClientConnectionGnutlsPrivate
+{
+ GTlsValidationFlags validation_flags;
+ GSocketConnectable *server_identity;
+ gboolean use_ssl3;
+
+ gboolean cert_requested;
+ char **accepted_cas;
+};
+
+static void
+g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsClientConnectionGnutlsPrivate));
+
+ gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
+ gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
+ gobject_class->finalize = g_tls_client_connection_gnutls_finalize;
+
+ connection_gnutls_class->begin_handshake = g_tls_client_connection_gnutls_begin_handshake;
+ connection_gnutls_class->finish_handshake = g_tls_client_connection_gnutls_finish_handshake;
+
+ 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");
+}
+
+static void
+g_tls_client_connection_gnutls_client_connection_interface_init (GTlsClientConnectionInterface *iface)
+{
+}
+
+static void
+g_tls_client_connection_gnutls_init (GTlsClientConnectionGnutls *gnutls)
+{
+ gnutls_certificate_credentials_t creds;
+
+ gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsPrivate);
+
+ creds = _g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
+ gnutls_certificate_client_set_retrieve_function (creds, g_tls_client_connection_gnutls_retrieve_function);
+}
+
+static void
+g_tls_client_connection_gnutls_finalize (GObject *object)
+{
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
+
+ if (gnutls->priv->server_identity)
+ g_object_unref (gnutls->priv->server_identity);
+ if (gnutls->priv->accepted_cas)
+ g_strfreev (gnutls->priv->accepted_cas);
+
+ G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_client_connection_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
+
+ switch (prop_id)
+ {
+ case PROP_VALIDATION_FLAGS:
+ g_value_set_flags (value, gnutls->priv->validation_flags);
+ break;
+
+ case PROP_SERVER_IDENTITY:
+ g_value_set_object (value, gnutls->priv->server_identity);
+ break;
+
+ case PROP_USE_SSL3:
+ g_value_set_boolean (value, gnutls->priv->use_ssl3);
+ break;
+
+ case PROP_ACCEPTED_CAS:
+ g_value_set_boxed (value, gnutls->priv->accepted_cas);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_client_connection_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object);
+
+ switch (prop_id)
+ {
+ case PROP_VALIDATION_FLAGS:
+ gnutls->priv->validation_flags = g_value_get_flags (value);
+ break;
+
+ case PROP_SERVER_IDENTITY:
+ if (gnutls->priv->server_identity)
+ g_object_unref (gnutls->priv->server_identity);
+ gnutls->priv->server_identity = g_value_dup_object (value);
+
+ if (G_IS_NETWORK_ADDRESS (gnutls->priv->server_identity))
+ {
+ const char *hostname;
+ gnutls_session_t session = _g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls));
+
+ hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity));
+ gnutls_server_name_set (session, GNUTLS_NAME_DNS,
+ hostname, strlen (hostname));
+ }
+ break;
+
+ case PROP_USE_SSL3:
+ gnutls->priv->use_ssl3 = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static int
+g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t session,
+ const gnutls_datum_t *req_ca_rdn,
+ int nreqs,
+ const gnutls_pk_algorithm_t *pk_algos,
+ int pk_algos_length,
+ gnutls_retr_st *st)
+{
+ GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
+ GPtrArray *accepted_cas;
+ int i;
+ char *buf, dummy[1];
+ size_t size;
+
+ gnutls->priv->cert_requested = TRUE;
+
+ accepted_cas = g_ptr_array_new ();
+ for (i = 0; i < nreqs; i++)
+ {
+ size = sizeof (dummy);
+ gnutls_x509_rdn_get (&req_ca_rdn[i], dummy, &size);
+ buf = g_malloc (size);
+ gnutls_x509_rdn_get (&req_ca_rdn[i], buf, &size);
+ g_ptr_array_add (accepted_cas, buf);
+ }
+ g_ptr_array_add (accepted_cas, NULL);
+
+ gnutls->priv->accepted_cas = (char **)accepted_cas->pdata;
+ g_ptr_array_free (accepted_cas, FALSE);
+
+ _g_tls_connection_gnutls_get_certificate (G_TLS_CONNECTION_GNUTLS (gnutls), st);
+ return 0;
+}
+
+static gboolean
+validate_handshake (GTlsClientConnectionGnutls *gnutls)
+{
+ GTlsValidationFlags errors;
+ gboolean accepted;
+
+ errors = _g_tls_connection_gnutls_validate_peer (G_TLS_CONNECTION_GNUTLS (gnutls));
+
+ /* FIXME: implement the full hostname/servicename/URI check
+ * according to draft-saintandre-tls-server-id-check
+ */
+ if ((gnutls->priv->validation_flags & G_TLS_VALIDATE_IDENTITY) &&
+ gnutls->priv->server_identity &&
+ G_IS_NETWORK_ADDRESS (gnutls->priv->server_identity))
+ {
+ gnutls_session session;
+ gnutls_x509_crt x509_cert;
+ const gnutls_datum_t *certs;
+ const char *hostname;
+ unsigned int num_certs;
+
+ session = _g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls));
+ hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity));
+
+ gnutls_x509_crt_init (&x509_cert);
+ certs = gnutls_certificate_get_peers (session, &num_certs);
+ gnutls_x509_crt_import (x509_cert, &certs[0], GNUTLS_X509_FMT_DER);
+ if (!gnutls_x509_crt_check_hostname (x509_cert, hostname))
+ errors |= G_TLS_VALIDATE_IDENTITY;
+ gnutls_x509_crt_deinit (x509_cert);
+ }
+
+ errors &= gnutls->priv->validation_flags;
+ if (errors)
+ {
+ GTlsCertificate *peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (gnutls));
+ accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer, errors);
+ }
+ else
+ accepted = TRUE;
+
+ return accepted;
+}
+
+static void
+g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+{
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+
+ gnutls->priv->cert_requested = FALSE;
+}
+
+static gboolean
+g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
+ gboolean success,
+ GError **inout_error)
+{
+ GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+
+ if (success)
+ {
+ if (validate_handshake (gnutls))
+ return TRUE;
+
+ g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ return FALSE;
+ }
+
+ if (g_error_matches (*inout_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
+ gnutls->priv->cert_requested)
+ {
+ g_clear_error (inout_error);
+ g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+ _("Server required TLS certificate"));
+ }
+ return FALSE;
+}
diff --git a/gio/gnutls/gtlsclientconnection-gnutls.h b/gio/gnutls/gtlsclientconnection-gnutls.h
new file mode 100644
index 0000000..90ce68e
--- /dev/null
+++ b/gio/gnutls/gtlsclientconnection-gnutls.h
@@ -0,0 +1,47 @@
+/* GIO - GLib ClientConnection, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_CLIENT_CONNECTION_GNUTLS_H__
+#define __G_TLS_CLIENT_CONNECTION_GNUTLS_H__
+
+#include <gio/gtlsclientconnection.h>
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS (_g_tls_client_connection_gnutls_get_type ())
+#define G_TLS_CLIENT_CONNECTION_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutls))
+#define G_TLS_CLIENT_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsClass))
+#define G_IS_TLS_CLIENT_CONNECTION_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS))
+#define G_IS_TLS_CLIENT_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS))
+#define G_TLS_CLIENT_CONNECTION_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CLIENT_CONNECTION_GNUTLS, GTlsClientConnectionGnutlsClass))
+
+typedef struct _GTlsClientConnectionGnutlsPrivate GTlsClientConnectionGnutlsPrivate;
+typedef struct _GTlsClientConnectionGnutlsClass GTlsClientConnectionGnutlsClass;
+typedef struct _GTlsClientConnectionGnutls GTlsClientConnectionGnutls;
+
+struct _GTlsClientConnectionGnutlsClass
+{
+ GTlsConnectionGnutlsClass parent_class;
+};
+
+struct _GTlsClientConnectionGnutls
+{
+ GTlsConnectionGnutls parent_instance;
+ GTlsClientConnectionGnutlsPrivate *priv;
+};
+
+GType _g_tls_client_connection_gnutls_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_CLIENT_CONNECTION_GNUTLS_H___ */
diff --git a/gio/gnutls/gtlsconnection-gnutls.c b/gio/gnutls/gtlsconnection-gnutls.c
new file mode 100644
index 0000000..8a9500d
--- /dev/null
+++ b/gio/gnutls/gtlsconnection-gnutls.c
@@ -0,0 +1,1186 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2009 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 <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsconnection-gnutls.h"
+#include "gasyncresult.h"
+#include "ginitable.h"
+#include "gioenumtypes.h"
+#include "gioerror.h"
+#include "gpollableinputstream.h"
+#include "gpollableoutputstream.h"
+#include "gsimpleasyncresult.h"
+#include "gtlsbackend-gnutls.h"
+#include "gtlscertificate-gnutls.h"
+#include "gtlsclientconnection.h"
+#include "gtlsinputstream-gnutls.h"
+#include "gtlsoutputstream-gnutls.h"
+#include "gtlsserverconnection-gnutls.h"
+#include "glibintl.h"
+
+static void g_tls_connection_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void g_tls_connection_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void g_tls_connection_gnutls_finalize (GObject *object);
+
+static gboolean g_tls_connection_gnutls_handshake (GTlsConnection *connection,
+ GCancellable *cancellable,
+ GError **error);
+static void g_tls_connection_gnutls_handshake_async (GTlsConnection *conn,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static gboolean g_tls_connection_gnutls_handshake_finish (GTlsConnection *conn,
+ GAsyncResult *result,
+ GError **error);
+
+static GInputStream *g_tls_connection_gnutls_get_input_stream (GIOStream *stream);
+static GOutputStream *g_tls_connection_gnutls_get_output_stream (GIOStream *stream);
+
+static gboolean g_tls_connection_gnutls_close (GIOStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+static void g_tls_connection_gnutls_close_async (GIOStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static gboolean g_tls_connection_gnutls_close_finish (GIOStream *stream,
+ GAsyncResult *result,
+ GError **error);
+
+static ssize_t g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t transport_data,
+ const void *buf,
+ size_t buflen);
+static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
+ void *buf,
+ size_t buflen);
+
+static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
+static gboolean g_tls_connection_gnutls_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error);
+
+#define g_tls_connection_gnutls_get_type _g_tls_connection_gnutls_get_type
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_connection_gnutls_initable_iface_init));
+
+
+enum
+{
+ PROP_0,
+ PROP_BASE_IO_STREAM,
+ PROP_CA_LIST,
+ PROP_REQUIRE_CLOSE_NOTIFY
+};
+
+struct _GTlsConnectionGnutlsPrivate
+{
+ GIOStream *base_io_stream;
+ GPollableInputStream *base_istream;
+ GPollableOutputStream *base_ostream;
+
+ GList *ca_list;
+ gboolean use_system_ca_list;
+ gnutls_certificate_credentials creds;
+ gnutls_session session;
+ gboolean require_close_notify;
+ gboolean need_handshake, handshaking, ever_handshaked;
+ gboolean closing;
+
+ GInputStream *tls_istream;
+ GOutputStream *tls_ostream;
+
+ GError *error;
+ GCancellable *cancellable;
+ gboolean blocking, eof;
+ GIOCondition internal_direction;
+};
+
+static void
+g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsConnectionClass *connection_class = G_TLS_CONNECTION_CLASS (klass);
+ GIOStreamClass *iostream_class = G_IO_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsConnectionGnutlsPrivate));
+
+ gobject_class->get_property = g_tls_connection_gnutls_get_property;
+ gobject_class->set_property = g_tls_connection_gnutls_set_property;
+ gobject_class->finalize = g_tls_connection_gnutls_finalize;
+
+ connection_class->handshake = g_tls_connection_gnutls_handshake;
+ connection_class->handshake_async = g_tls_connection_gnutls_handshake_async;
+ connection_class->handshake_finish = g_tls_connection_gnutls_handshake_finish;
+
+ iostream_class->get_input_stream = g_tls_connection_gnutls_get_input_stream;
+ iostream_class->get_output_stream = g_tls_connection_gnutls_get_output_stream;
+ iostream_class->close_fn = g_tls_connection_gnutls_close;
+ iostream_class->close_async = g_tls_connection_gnutls_close_async;
+ iostream_class->close_finish = g_tls_connection_gnutls_close_finish;
+
+ 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_CA_LIST, "ca-list");
+}
+
+static void
+g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_connection_gnutls_initable_init;
+}
+
+static void
+g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
+{
+ gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsPrivate);
+
+ gnutls_certificate_allocate_credentials (&gnutls->priv->creds);
+ gnutls_certificate_set_verify_flags (gnutls->priv->creds,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+ gnutls->priv->need_handshake = TRUE;
+ gnutls->priv->use_system_ca_list = TRUE;
+}
+
+static gboolean
+g_tls_connection_gnutls_initable_init (GInitable *initable,
+ GCancellable *cancellable, GError **error)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
+ gboolean client, use_ssl3 = FALSE;
+ int status;
+
+ /* Make sure gnutls->priv->session has been initialized (it may have
+ * already been initialized by a construct-time property setter).
+ */
+ _g_tls_connection_gnutls_get_session (gnutls);
+
+ client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+ if (client)
+ g_object_get (G_OBJECT (gnutls), "use-ssl3", &use_ssl3, NULL);
+ if (use_ssl3)
+ {
+ status = gnutls_priority_set_direct (gnutls->priv->session,
+ "NORMAL:!VERS-TLS1.2:!VERS-TLS1.1:!VERS-TLS1.0",
+ NULL);
+ }
+ else
+ {
+ status = gnutls_priority_set_direct (gnutls->priv->session,
+ "NORMAL", NULL);
+ }
+ if (status != 0)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Could not create TLS connection: %s"),
+ gnutls_strerror (status));
+ return FALSE;
+ }
+
+ if (gnutls->priv->use_system_ca_list)
+ {
+ gnutls_x509_crt_t *cas;
+ int num_cas;
+
+ g_tls_backend_gnutls_get_system_ca_list_gnutls (&cas, &num_cas);
+ gnutls_certificate_set_x509_trust (gnutls->priv->creds, cas, num_cas);
+ }
+
+ status = gnutls_credentials_set (gnutls->priv->session,
+ GNUTLS_CRD_CERTIFICATE,
+ gnutls->priv->creds);
+ if (status != 0)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Could not create TLS connection: %s"),
+ gnutls_strerror (status));
+ return FALSE;
+ }
+
+ gnutls_transport_set_push_function (gnutls->priv->session,
+ g_tls_connection_gnutls_push_func);
+ gnutls_transport_set_pull_function (gnutls->priv->session,
+ g_tls_connection_gnutls_pull_func);
+ gnutls_transport_set_ptr (gnutls->priv->session, gnutls);
+
+ gnutls->priv->tls_istream = _g_tls_input_stream_gnutls_new (gnutls);
+ gnutls->priv->tls_ostream = _g_tls_output_stream_gnutls_new (gnutls);
+
+ return TRUE;
+}
+
+static void
+g_tls_connection_gnutls_finalize (GObject *object)
+{
+ GTlsConnectionGnutls *connection = G_TLS_CONNECTION_GNUTLS (object);
+
+ if (connection->priv->base_io_stream)
+ g_object_unref (connection->priv->base_io_stream);
+
+ if (connection->priv->session)
+ gnutls_deinit (connection->priv->session);
+
+ if (connection->priv->tls_istream)
+ g_object_unref (connection->priv->tls_istream);
+ if (connection->priv->tls_ostream)
+ g_object_unref (connection->priv->tls_ostream);
+
+ if (connection->priv->creds)
+ gnutls_certificate_free_credentials (connection->priv->creds);
+
+ if (connection->priv->error)
+ g_error_free (connection->priv->error);
+
+ G_OBJECT_CLASS (g_tls_connection_gnutls_parent_class)->finalize (object);
+}
+
+static void
+g_tls_connection_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
+ const GList *ca_list, *c;
+ GList *copy;
+
+ switch (prop_id)
+ {
+ case PROP_BASE_IO_STREAM:
+ g_value_set_object (value, gnutls->priv->base_io_stream);
+ break;
+
+ case PROP_CA_LIST:
+ if (gnutls->priv->use_system_ca_list)
+ ca_list = g_tls_backend_gnutls_get_system_ca_list_gtls ();
+ else
+ ca_list = gnutls->priv->ca_list;
+ for (c = ca_list, copy = NULL; c; c = c->next)
+ copy = g_list_prepend (copy, g_object_ref (c->data));
+ g_value_set_pointer (value, g_list_reverse (copy));
+ break;
+
+ case PROP_REQUIRE_CLOSE_NOTIFY:
+ g_value_set_boolean (value, gnutls->priv->require_close_notify);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_connection_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
+ GList *list, *copy, *c;
+
+ switch (prop_id)
+ {
+ case PROP_BASE_IO_STREAM:
+ if (gnutls->priv->base_io_stream)
+ g_object_unref (gnutls->priv->base_io_stream);
+ gnutls->priv->base_io_stream = g_value_dup_object (value);
+ if (!gnutls->priv->base_io_stream)
+ return;
+
+ gnutls->priv->base_istream = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (gnutls->priv->base_io_stream));
+ gnutls->priv->base_ostream = G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (gnutls->priv->base_io_stream));
+ break;
+
+ case PROP_CA_LIST:
+ if (gnutls->priv->ca_list)
+ {
+ for (c = gnutls->priv->ca_list; c; c = c->next)
+ g_object_unref (c->data);
+ g_list_free (gnutls->priv->ca_list);
+ }
+
+ list = g_value_get_pointer (value);
+ copy = NULL;
+ for (c = list; c; c = c->next)
+ copy = g_list_prepend (copy, g_object_ref (c->data));
+ gnutls->priv->ca_list = g_list_reverse (copy);
+ gnutls->priv->use_system_ca_list = FALSE;
+
+ /* Update the creds */
+ gnutls_certificate_free_cas (gnutls->priv->creds);
+ if (gnutls->priv->ca_list)
+ {
+ gnutls_x509_crt_t *gnutls_cas;
+ int num_cas, i;
+
+ num_cas = g_list_length (gnutls->priv->ca_list);
+ gnutls_cas = g_new (gnutls_x509_crt_t, num_cas);
+ for (c = gnutls->priv->ca_list, i = 0; c; c = c->next, i++)
+ gnutls_cas[i] = g_tls_certificate_gnutls_get_cert (c->data);
+
+ gnutls_certificate_set_x509_trust (gnutls->priv->creds,
+ gnutls_cas, num_cas);
+ g_free (gnutls_cas);
+ }
+ break;
+
+ case PROP_REQUIRE_CLOSE_NOTIFY:
+ gnutls->priv->require_close_notify = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+gnutls_certificate_credentials
+_g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
+{
+ return gnutls->priv->creds;
+}
+
+gnutls_session
+_g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *gnutls)
+{
+ /* Ideally we would initialize gnutls->priv->session from
+ * g_tls_connection_gnutls_init(), but we can't tell if it's a
+ * client or server connection at that point... And
+ * g_tls_connection_gnutls_initiable_init() is too late, because
+ * construct-time property setters may need to modify it.
+ */
+ if (!gnutls->priv->session)
+ {
+ gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
+ gnutls_init (&gnutls->priv->session, client ? GNUTLS_CLIENT : GNUTLS_SERVER);
+ }
+
+ return gnutls->priv->session;
+}
+
+void
+_g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
+ gnutls_retr_st *st)
+{
+ GTlsCertificate *cert;
+
+ cert = g_tls_connection_get_certificate (G_TLS_CONNECTION (gnutls));
+ if (cert)
+ g_object_ref (cert);
+ else
+ cert = g_tls_connection_emit_need_certificate (G_TLS_CONNECTION (gnutls));
+
+ st->type = GNUTLS_CRT_X509;
+ if (cert)
+ {
+ GTlsCertificateGnutls *gnutlscert = G_TLS_CERTIFICATE_GNUTLS (cert);
+
+ st->ncerts = 1;
+ st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t));
+ st->cert.x509[0] = g_tls_certificate_gnutls_copy_cert (gnutlscert);
+ st->key.x509 = g_tls_certificate_gnutls_copy_key (gnutlscert);
+ st->deinit_all = TRUE;
+
+ g_object_unref (cert);
+ }
+ else
+ st->ncerts = 0;
+}
+
+static const struct {
+ int gnutls_flag;
+ GTlsValidationFlags gtls_flag;
+} flags_map[] = {
+ { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_VALIDATE_CA },
+ { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_VALIDATE_ACTIVATION },
+ { GNUTLS_CERT_EXPIRED, G_TLS_VALIDATE_EXPIRATION },
+ { GNUTLS_CERT_REVOKED, G_TLS_VALIDATE_REVOCATION },
+ { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_VALIDATE_ALGORITHM }
+};
+static const int flags_map_size = G_N_ELEMENTS (flags_map);
+
+GTlsValidationFlags
+_g_tls_connection_gnutls_validate_peer (GTlsConnectionGnutls *gnutls)
+{
+ int status, i;
+ GTlsValidationFlags gtls_errors;
+
+ status = gnutls_certificate_verify_peers (gnutls->priv->session);
+
+ /* Convert GNUTLS status to GTlsValidationFlags. GNUTLS sets
+ * GNUTLS_CERT_INVALID if it sets any other flag, so we want to
+ * strip that out unless it's the only flag set. Then we convert
+ * specific flags we recognize, and if there are any flags left over
+ * at the end, we add G_TLS_VALIDATE_GENERIC_ERROR.
+ */
+ gtls_errors = 0;
+
+ if (status != GNUTLS_CERT_INVALID)
+ status = status & ~GNUTLS_CERT_INVALID;
+ for (i = 0; i < flags_map_size && status != 0; i++)
+ {
+ if (status & flags_map[i].gnutls_flag)
+ {
+ status &= ~flags_map[i].gnutls_flag;
+ gtls_errors |= flags_map[i].gtls_flag;
+ }
+ }
+ if (status)
+ gtls_errors |= G_TLS_VALIDATE_GENERIC_ERROR;
+
+ return gtls_errors;
+}
+
+void
+begin_gnutls_io (GTlsConnectionGnutls *gnutls,
+ gboolean blocking,
+ GCancellable *cancellable)
+{
+ gnutls->priv->blocking = blocking;
+ gnutls->priv->cancellable = cancellable;
+ gnutls->priv->internal_direction = 0;
+ g_clear_error (&gnutls->priv->error);
+}
+
+int
+end_gnutls_io (GTlsConnectionGnutls *gnutls,
+ int status,
+ const char *generic_error,
+ GError **error)
+{
+ gnutls->priv->cancellable = NULL;
+
+ if (status >= 0)
+ {
+ g_clear_error (&gnutls->priv->error);
+ return status;
+ }
+
+ if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+ {
+ if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+ status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+ status == GNUTLS_E_FATAL_ALERT_RECEIVED ||
+ status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+ {
+ g_clear_error (&gnutls->priv->error);
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+ _("Peer failed to perform TLS handshake"));
+ return GNUTLS_E_PULL_ERROR;
+ }
+ }
+
+ if (gnutls->priv->error)
+ {
+ if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ status = GNUTLS_E_AGAIN;
+ g_propagate_error (error, gnutls->priv->error);
+ gnutls->priv->error = NULL;
+ return status;
+ }
+ else if (status == GNUTLS_E_REHANDSHAKE)
+ {
+ gnutls->priv->need_handshake = TRUE;
+ return status;
+ }
+ else if (status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+ {
+ if (gnutls->priv->eof)
+ {
+ if (gnutls->priv->require_close_notify)
+ {
+ g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
+ _("TLS connection closed unexpectedly"));
+ return status;
+ }
+ else
+ return 0;
+ }
+ }
+
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ generic_error, gnutls_strerror (status));
+ return status;
+}
+
+#define BEGIN_GNUTLS_IO(gnutls, blocking, cancellable) \
+ begin_gnutls_io (gnutls, blocking, cancellable); \
+ do {
+
+#define END_GNUTLS_IO(gnutls, ret, errmsg, error) \
+ } while ((ret == GNUTLS_E_AGAIN || \
+ ret == GNUTLS_E_WARNING_ALERT_RECEIVED) && \
+ !gnutls->priv->error); \
+ ret = end_gnutls_io (gnutls, ret, errmsg, error)
+
+gboolean
+_g_tls_connection_gnutls_check (GTlsConnectionGnutls *gnutls,
+ GIOCondition condition)
+{
+ if (gnutls->priv->handshaking || gnutls->priv->closing)
+ condition = gnutls->priv->internal_direction;
+
+ if (condition & G_IO_IN)
+ return g_pollable_input_stream_is_readable (gnutls->priv->base_istream);
+ else
+ return g_pollable_output_stream_is_writable (gnutls->priv->base_ostream);
+}
+
+typedef struct {
+ GSource source;
+
+ GTlsConnectionGnutls *gnutls;
+ GObject *stream;
+
+ GSource *child_source;
+ GIOCondition base_direction;
+ GIOCondition current_direction;
+} GTlsConnectionGnutlsSource;
+
+static gboolean
+gnutls_source_prepare (GSource *source,
+ gint *timeout)
+{
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean
+gnutls_source_check (GSource *source)
+{
+ return FALSE;
+}
+
+static void
+gnutls_source_sync_child_source (GTlsConnectionGnutlsSource *gnutls_source,
+ GIOCondition direction)
+{
+ GTlsConnectionGnutls *gnutls = gnutls_source->gnutls;
+ GSource *source = (GSource *)gnutls_source;
+
+ if (direction == gnutls_source->current_direction)
+ return;
+
+ if (gnutls_source->child_source)
+ {
+ g_source_remove_child_source (source, gnutls_source->child_source);
+ g_source_unref (gnutls_source->child_source);
+ }
+
+ if (direction & G_IO_IN)
+ gnutls_source->child_source = g_pollable_input_stream_create_source (gnutls->priv->base_istream, NULL);
+ else
+ gnutls_source->child_source = g_pollable_output_stream_create_source (gnutls->priv->base_ostream, NULL);
+
+ g_source_add_child_source (source, gnutls_source->child_source);
+ gnutls_source->current_direction = direction;
+}
+
+static gboolean
+gnutls_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GPollableSourceFunc func = (GPollableSourceFunc)callback;
+ GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
+ GTlsConnectionGnutls *gnutls = gnutls_source->gnutls;
+ gboolean ret;
+
+ ret = (*func) (gnutls_source->stream, user_data);
+ if (ret)
+ {
+ GIOCondition direction = gnutls->priv->internal_direction ? gnutls->priv->internal_direction : gnutls_source->base_direction;
+
+ gnutls_source_sync_child_source (gnutls_source, direction);
+ }
+
+ return ret;
+}
+
+static void
+gnutls_source_finalize (GSource *source)
+{
+ GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
+
+ g_object_unref (gnutls_source->gnutls);
+
+ if (gnutls_source->child_source)
+ g_source_unref (gnutls_source->child_source);
+}
+
+static GSourceFuncs gnutls_source_funcs =
+{
+ gnutls_source_prepare,
+ gnutls_source_check,
+ gnutls_source_dispatch,
+ gnutls_source_finalize
+};
+
+GSource *
+_g_tls_connection_gnutls_create_source (GTlsConnectionGnutls *gnutls,
+ GIOCondition condition,
+ GCancellable *cancellable)
+{
+ GSource *source, *cancellable_source;
+ GTlsConnectionGnutlsSource *gnutls_source;
+
+ source = g_source_new (&gnutls_source_funcs, sizeof (GTlsConnectionGnutlsSource));
+ g_source_set_name (source, "GTlsConnectionGnutlsSource");
+ gnutls_source = (GTlsConnectionGnutlsSource *)source;
+ gnutls_source->gnutls = g_object_ref (gnutls);
+ gnutls_source->base_direction = condition & (G_IO_IN | G_IO_OUT);
+ if (gnutls_source->base_direction == G_IO_IN)
+ gnutls_source->stream = G_OBJECT (gnutls->priv->tls_istream);
+ else if (gnutls_source->base_direction == G_IO_OUT)
+ gnutls_source->stream = G_OBJECT (gnutls->priv->tls_ostream);
+ else
+ {
+ gnutls_source->base_direction = gnutls->priv->internal_direction;
+ gnutls_source->stream = NULL;
+ }
+ gnutls_source_sync_child_source (gnutls_source, gnutls_source->base_direction);
+
+ if (cancellable)
+ {
+ cancellable_source = g_cancellable_source_new (cancellable);
+ g_source_add_child_source (source, cancellable_source);
+ g_source_unref (cancellable_source);
+ }
+
+ return source;
+}
+
+static void
+set_gnutls_error (GTlsConnectionGnutls *gnutls, GIOCondition direction)
+{
+ if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ gnutls_transport_set_errno (gnutls->priv->session, EINTR);
+ else if (g_error_matches (gnutls->priv->error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ gnutls_transport_set_errno (gnutls->priv->session, EAGAIN);
+ gnutls->priv->internal_direction = direction;
+ }
+ else
+ gnutls_transport_set_errno (gnutls->priv->session, EIO);
+}
+
+static ssize_t
+g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
+ void *buf,
+ size_t buflen)
+{
+ GTlsConnectionGnutls *gnutls = transport_data;
+ ssize_t ret;
+
+ /* If gnutls->priv->error is non-%NULL when we're called, it means
+ * that an error previously occurred, but gnutls decided not to
+ * propagate it. So it's correct for us to just clear it. (Usually
+ * this means it ignored an EAGAIN after a short read, and now
+ * we'll return EAGAIN again, which it will obey this time.)
+ */
+ g_clear_error (&gnutls->priv->error);
+
+ if (gnutls->priv->blocking)
+ {
+ ret = g_input_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
+ buf, buflen,
+ gnutls->priv->cancellable,
+ &gnutls->priv->error);
+ }
+ else
+ {
+ ret = g_pollable_input_stream_read_nonblocking (gnutls->priv->base_istream,
+ buf, buflen,
+ gnutls->priv->cancellable,
+ &gnutls->priv->error);
+ }
+
+ if (ret < 0)
+ set_gnutls_error (gnutls, G_IO_IN);
+ else if (ret == 0)
+ gnutls->priv->eof = TRUE;
+
+ return ret;
+}
+
+static ssize_t
+g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t transport_data,
+ const void *buf,
+ size_t buflen)
+{
+ GTlsConnectionGnutls *gnutls = transport_data;
+ ssize_t ret;
+
+ /* See comment in pull_func. */
+ g_clear_error (&gnutls->priv->error);
+
+ if (gnutls->priv->blocking)
+ {
+ ret = g_output_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
+ buf, buflen,
+ gnutls->priv->cancellable,
+ &gnutls->priv->error);
+ }
+ else
+ {
+ ret = g_pollable_output_stream_write_nonblocking (gnutls->priv->base_ostream,
+ buf, buflen,
+ gnutls->priv->cancellable,
+ &gnutls->priv->error);
+ }
+ if (ret < 0)
+ set_gnutls_error (gnutls, G_IO_OUT);
+
+ return ret;
+}
+
+static gboolean
+handshake_internal (GTlsConnectionGnutls *gnutls,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ int ret;
+
+ if (G_IS_TLS_SERVER_CONNECTION_GNUTLS (gnutls) &&
+ gnutls->priv->ever_handshaked && !gnutls->priv->need_handshake)
+ {
+ BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+ ret = gnutls_rehandshake (gnutls->priv->session);
+ END_GNUTLS_IO (gnutls, ret, _("Error performing TLS handshake: %s"), error);
+
+ if (ret != 0)
+ return FALSE;
+ }
+
+ gnutls->priv->handshaking = TRUE;
+ G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
+
+ BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+ ret = gnutls_handshake (gnutls->priv->session);
+ END_GNUTLS_IO (gnutls, ret, _("Error performing TLS handshake: %s"), error);
+
+ if (ret == GNUTLS_E_AGAIN)
+ return FALSE;
+
+ gnutls->priv->handshaking = FALSE;
+ gnutls->priv->need_handshake = FALSE;
+ gnutls->priv->ever_handshaked = TRUE;
+
+ if (ret == 0 &&
+ gnutls_certificate_type_get (gnutls->priv->session) == GNUTLS_CRT_X509)
+ {
+ GTlsCertificate *chain, *cert;
+ const gnutls_datum_t *certs;
+ unsigned int num_certs;
+ int i;
+
+ certs = gnutls_certificate_get_peers (gnutls->priv->session, &num_certs);
+ chain = NULL;
+ if (certs)
+ {
+ for (i = num_certs - 1; i >= 0; i--)
+ {
+ cert = g_tls_certificate_gnutls_new (&certs[i], chain);
+ chain = cert;
+ }
+ }
+
+ g_tls_connection_set_peer_certificate (G_TLS_CONNECTION (gnutls), chain);
+ }
+ else
+ g_tls_connection_set_peer_certificate (G_TLS_CONNECTION (gnutls), NULL);
+
+ return G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, ret == 0, error);
+}
+
+static gboolean
+handshake_in_progress_or_failed (GTlsConnectionGnutls *gnutls,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (!(gnutls->priv->need_handshake || gnutls->priv->handshaking))
+ return FALSE;
+
+ return !handshake_internal (gnutls, blocking, cancellable, error);
+}
+
+static gboolean
+g_tls_connection_gnutls_handshake (GTlsConnection *conn,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
+
+ return handshake_internal (gnutls, TRUE, cancellable, error);
+}
+
+static gboolean
+g_tls_connection_gnutls_handshake_ready (GObject *pollable_stream,
+ gpointer user_data)
+{
+ GTlsConnectionGnutls *gnutls;
+ GSimpleAsyncResult *simple = user_data;
+ gboolean success;
+ GError *error = NULL;
+
+ gnutls = G_TLS_CONNECTION_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ g_object_unref (gnutls);
+
+ success = handshake_internal (gnutls, FALSE, NULL, &error);
+ if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_error_free (error);
+ return TRUE;
+ }
+
+ if (error)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, success);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+
+ return FALSE;
+}
+
+static void
+g_tls_connection_gnutls_handshake_async (GTlsConnection *conn,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
+ GSimpleAsyncResult *simple;
+ gboolean success;
+ GError *error = NULL;
+ GSource *source;
+
+ simple = g_simple_async_result_new (G_OBJECT (conn), callback, user_data,
+ g_tls_connection_gnutls_handshake_async);
+ success = handshake_internal (gnutls, FALSE, cancellable, &error);
+ if (success)
+ {
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+
+ source = _g_tls_connection_gnutls_create_source (gnutls, 0, cancellable);
+ g_source_set_callback (source,
+ (GSourceFunc) g_tls_connection_gnutls_handshake_ready,
+ simple, NULL);
+ g_source_set_priority (source, io_priority);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+}
+
+static gboolean
+g_tls_connection_gnutls_handshake_finish (GTlsConnection *conn,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (conn), g_tls_connection_gnutls_handshake_async), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+gssize
+_g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
+ void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gssize ret;
+
+ again:
+ if (handshake_in_progress_or_failed (gnutls, blocking, cancellable, error))
+ return -1;
+
+ BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+ ret = gnutls_record_recv (gnutls->priv->session, buffer, count);
+ END_GNUTLS_IO (gnutls, ret, _("Error reading data from TLS socket: %s"), error);
+
+ if (ret >= 0)
+ return ret;
+ else if (ret == GNUTLS_E_REHANDSHAKE)
+ goto again;
+ else
+ return -1;
+}
+
+gssize
+_g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gssize ret;
+
+ again:
+ if (handshake_in_progress_or_failed (gnutls, blocking, cancellable, error))
+ return -1;
+
+ BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+ ret = gnutls_record_send (gnutls->priv->session, buffer, count);
+ END_GNUTLS_IO (gnutls, ret, _("Error writing data to TLS socket: %s"), error);
+
+ if (ret >= 0)
+ return ret;
+ else if (ret == GNUTLS_E_REHANDSHAKE)
+ goto again;
+ else
+ return -1;
+}
+
+static GInputStream *
+g_tls_connection_gnutls_get_input_stream (GIOStream *stream)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+
+ return gnutls->priv->tls_istream;
+}
+
+static GOutputStream *
+g_tls_connection_gnutls_get_output_stream (GIOStream *stream)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+
+ return gnutls->priv->tls_ostream;
+}
+
+static gboolean
+close_internal (GTlsConnectionGnutls *gnutls,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ int ret;
+
+ if (!gnutls->priv->require_close_notify)
+ return TRUE;
+
+ /* If we haven't finished the initial handshake yet, there's no
+ * reason to finish it just so we can close.
+ */
+ if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+ return TRUE;
+
+ if (handshake_in_progress_or_failed (gnutls, blocking, cancellable, error))
+ return FALSE;
+
+ gnutls->priv->closing = TRUE;
+ BEGIN_GNUTLS_IO (gnutls, blocking, cancellable);
+ ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
+ END_GNUTLS_IO (gnutls, ret, _("Error performing TLS close: %s"), error);
+ if (ret == 0 || !error || !g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ gnutls->priv->closing = FALSE;
+
+ return ret == 0;
+}
+
+static gboolean
+g_tls_connection_gnutls_close (GIOStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+
+ if (!close_internal (gnutls, TRUE, cancellable, error))
+ return FALSE;
+ return g_io_stream_close (gnutls->priv->base_io_stream,
+ cancellable, error);
+}
+
+typedef struct {
+ GSimpleAsyncResult *simple;
+ GCancellable *cancellable;
+ int io_priority;
+} AsyncCloseData;
+
+static void
+close_base_stream_cb (GObject *base_stream,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ gboolean success;
+ GError *error = NULL;
+ AsyncCloseData *acd = user_data;
+
+ success = g_io_stream_close_finish (G_IO_STREAM (base_stream),
+ result, &error);
+ if (success)
+ g_simple_async_result_set_op_res_gboolean (acd->simple, TRUE);
+ else
+ {
+ g_simple_async_result_set_from_error (acd->simple, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete (acd->simple);
+ g_object_unref (acd->simple);
+ if (acd->cancellable)
+ g_object_unref (acd->cancellable);
+ g_slice_free (AsyncCloseData, acd);
+}
+
+static gboolean
+g_tls_connection_gnutls_close_ready (GObject *pollable_stream,
+ gpointer user_data)
+{
+ GTlsConnectionGnutls *gnutls;
+ AsyncCloseData *acd = user_data;
+ gboolean success;
+ GError *error = NULL;
+
+ gnutls = G_TLS_CONNECTION_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (acd->simple)));
+ g_object_unref (gnutls);
+
+ success = close_internal (gnutls, FALSE, NULL, &error);
+ if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_error_free (error);
+ return TRUE;
+ }
+
+ if (error)
+ {
+ g_simple_async_result_set_from_error (acd->simple, error);
+ g_simple_async_result_complete (acd->simple);
+ g_error_free (error);
+ g_object_unref (acd->simple);
+ if (acd->cancellable)
+ g_object_unref (acd->cancellable);
+ g_slice_free (AsyncCloseData, acd);
+ }
+ else
+ {
+ g_io_stream_close_async (gnutls->priv->base_io_stream,
+ acd->io_priority, acd->cancellable,
+ close_base_stream_cb, acd);
+ }
+
+ return FALSE;
+}
+
+static void
+g_tls_connection_gnutls_close_async (GIOStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
+ GSimpleAsyncResult *simple;
+ gboolean success;
+ GError *error = NULL;
+ AsyncCloseData *acd;
+ GSource *source;
+
+ simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+ g_tls_connection_gnutls_close_async);
+
+ success = close_internal (gnutls, FALSE, cancellable, &error);
+ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ acd = g_slice_new (AsyncCloseData);
+ acd->simple = simple;
+ acd->cancellable = g_object_ref (cancellable);
+ acd->io_priority = io_priority;
+
+ if (success)
+ {
+ g_io_stream_close_async (gnutls->priv->base_io_stream,
+ io_priority, cancellable,
+ close_base_stream_cb, acd);
+ return;
+ }
+
+ source = _g_tls_connection_gnutls_create_source (gnutls, 0, acd->cancellable);
+ g_source_set_callback (source,
+ (GSourceFunc) g_tls_connection_gnutls_close_ready,
+ acd, NULL);
+ g_source_set_priority (source, acd->io_priority);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+}
+
+static gboolean
+g_tls_connection_gnutls_close_finish (GIOStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_connection_gnutls_close_async), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
diff --git a/gio/gnutls/gtlsconnection-gnutls.h b/gio/gnutls/gtlsconnection-gnutls.h
new file mode 100644
index 0000000..6227d7d
--- /dev/null
+++ b/gio/gnutls/gtlsconnection-gnutls.h
@@ -0,0 +1,77 @@
+/* GIO - GLib Connection, Output and Gnutlsing Library
+ *
+ * Copyright © 2009 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_CONNECTION_GNUTLS_H__
+#define __G_TLS_CONNECTION_GNUTLS_H__
+
+#include <gio/gtlsconnection.h>
+#include <gnutls/gnutls.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION_GNUTLS (_g_tls_connection_gnutls_get_type ())
+#define G_TLS_CONNECTION_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutls))
+#define G_TLS_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsClass))
+#define G_IS_TLS_CONNECTION_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CONNECTION_GNUTLS))
+#define G_IS_TLS_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION_GNUTLS))
+#define G_TLS_CONNECTION_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CONNECTION_GNUTLS, GTlsConnectionGnutlsClass))
+
+typedef struct _GTlsConnectionGnutlsPrivate GTlsConnectionGnutlsPrivate;
+typedef struct _GTlsConnectionGnutlsClass GTlsConnectionGnutlsClass;
+typedef struct _GTlsConnectionGnutls GTlsConnectionGnutls;
+
+struct _GTlsConnectionGnutlsClass
+{
+ GTlsConnectionClass parent_class;
+
+ void (*begin_handshake) (GTlsConnectionGnutls *gnutls);
+ gboolean (*finish_handshake) (GTlsConnectionGnutls *gnutls,
+ gboolean success,
+ GError **inout_error);
+};
+
+struct _GTlsConnectionGnutls
+{
+ GTlsConnection parent_instance;
+ GTlsConnectionGnutlsPrivate *priv;
+};
+
+GType _g_tls_connection_gnutls_get_type (void) G_GNUC_CONST;
+
+gnutls_certificate_credentials _g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
+gnutls_session _g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *connection);
+void _g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
+ gnutls_retr_st *st);
+GTlsValidationFlags _g_tls_connection_gnutls_validate_peer (GTlsConnectionGnutls *gnutls);
+
+gssize _g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
+ void *buffer,
+ gsize size,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+gssize _g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
+ const void *buffer,
+ gsize size,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean _g_tls_connection_gnutls_check (GTlsConnectionGnutls *gnutls,
+ GIOCondition condition);
+GSource *_g_tls_connection_gnutls_create_source (GTlsConnectionGnutls *gnutls,
+ GIOCondition condition,
+ GCancellable *cancellable);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_GNUTLS_H___ */
diff --git a/gio/gnutls/gtlsinputstream-gnutls.c b/gio/gnutls/gtlsinputstream-gnutls.c
new file mode 100644
index 0000000..8c992f5
--- /dev/null
+++ b/gio/gnutls/gtlsinputstream-gnutls.c
@@ -0,0 +1,240 @@
+/* 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 "gtlsinputstream-gnutls.h"
+#include "gasyncresult.h"
+#include "gcancellable.h"
+#include "gioerror.h"
+#include "gpollableinputstream.h"
+#include "gsimpleasyncresult.h"
+#include "gtlsconnection-gnutls.h"
+#include "glibintl.h"
+
+static void g_tls_input_stream_gnutls_pollable_iface_init (GPollableInputStreamInterface *iface);
+
+#define g_tls_input_stream_gnutls_get_type _g_tls_input_stream_gnutls_get_type
+G_DEFINE_TYPE_WITH_CODE (GTlsInputStreamGnutls, g_tls_input_stream_gnutls, G_TYPE_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, g_tls_input_stream_gnutls_pollable_iface_init)
+ )
+
+struct _GTlsInputStreamGnutlsPrivate
+{
+ GTlsConnectionGnutls *conn;
+
+ /* pending operation metadata */
+ GCancellable *cancellable;
+ gpointer buffer;
+ gsize count;
+};
+
+static void
+g_tls_input_stream_gnutls_finalize (GObject *object)
+{
+ GTlsInputStreamGnutls *stream = G_TLS_INPUT_STREAM_GNUTLS (object);
+
+ if (stream->priv->conn)
+ g_object_unref (stream->priv->conn);
+
+ G_OBJECT_CLASS (g_tls_input_stream_gnutls_parent_class)->finalize (object);
+}
+
+static gssize
+g_tls_input_stream_gnutls_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (stream);
+
+ return _g_tls_connection_gnutls_read (tls_stream->priv->conn,
+ buffer, count, TRUE,
+ cancellable, error);
+}
+
+static gboolean
+g_tls_input_stream_gnutls_read_ready (GPollableInputStream *stream,
+ gpointer user_data)
+{
+ GTlsInputStreamGnutls *tls_stream;
+ GSimpleAsyncResult *simple = user_data;
+ gssize nread;
+ GError *error = NULL;
+
+ tls_stream = G_TLS_INPUT_STREAM_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ g_object_unref (tls_stream);
+
+ nread = _g_tls_connection_gnutls_read (tls_stream->priv->conn,
+ tls_stream->priv->buffer,
+ tls_stream->priv->count, FALSE,
+ tls_stream->priv->cancellable,
+ &error);
+ if (nread == -1 &&
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_error_free (error);
+ return TRUE;
+ }
+
+ if (error)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+ else
+ g_simple_async_result_set_op_res_gssize (simple, nread);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+
+ return FALSE;
+}
+
+static void
+g_tls_input_stream_gnutls_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (stream);
+ GSimpleAsyncResult *simple;
+ gssize nread;
+ GError *error = NULL;
+ GSource *source;
+
+ simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+ g_tls_input_stream_gnutls_read_async);
+ nread = _g_tls_connection_gnutls_read (tls_stream->priv->conn,
+ buffer, count, FALSE,
+ cancellable, &error);
+
+ if (nread >= 0 ||
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ if (error)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+ else
+ g_simple_async_result_set_op_res_gssize (simple, nread);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ tls_stream->priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ tls_stream->priv->buffer = buffer;
+ tls_stream->priv->count = count;
+
+ source = _g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+ G_IO_IN,
+ tls_stream->priv->cancellable);
+ g_source_set_callback (source,
+ (GSourceFunc) g_tls_input_stream_gnutls_read_ready,
+ simple, NULL);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+}
+
+static gssize
+g_tls_input_stream_gnutls_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_TLS_INPUT_STREAM_GNUTLS (stream), -1);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_input_stream_gnutls_read_async), -1);
+
+ return g_simple_async_result_get_op_res_gssize (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+static gboolean
+g_tls_input_stream_gnutls_pollable_is_readable (GPollableInputStream *pollable)
+{
+ GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (pollable);
+
+ return _g_tls_connection_gnutls_check (tls_stream->priv->conn, G_IO_IN);
+}
+
+static GSource *
+g_tls_input_stream_gnutls_pollable_create_source (GPollableInputStream *pollable,
+ GCancellable *cancellable)
+{
+ GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (pollable);
+
+ return _g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+ G_IO_IN,
+ cancellable);
+}
+
+static gssize
+g_tls_input_stream_gnutls_pollable_read_nonblocking (GPollableInputStream *pollable,
+ void *buffer,
+ gsize size,
+ GError **error)
+{
+ GTlsInputStreamGnutls *tls_stream = G_TLS_INPUT_STREAM_GNUTLS (pollable);
+
+ return _g_tls_connection_gnutls_read (tls_stream->priv->conn,
+ buffer, size, FALSE,
+ NULL, error);
+}
+
+static void
+g_tls_input_stream_gnutls_class_init (GTlsInputStreamGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsInputStreamGnutlsPrivate));
+
+ gobject_class->finalize = g_tls_input_stream_gnutls_finalize;
+
+ input_stream_class->read_fn = g_tls_input_stream_gnutls_read;
+ input_stream_class->read_async = g_tls_input_stream_gnutls_read_async;
+ input_stream_class->read_finish = g_tls_input_stream_gnutls_read_finish;
+}
+
+static void
+g_tls_input_stream_gnutls_pollable_iface_init (GPollableInputStreamInterface *iface)
+{
+ iface->is_readable = g_tls_input_stream_gnutls_pollable_is_readable;
+ iface->create_source = g_tls_input_stream_gnutls_pollable_create_source;
+ iface->read_nonblocking = g_tls_input_stream_gnutls_pollable_read_nonblocking;
+}
+
+static void
+g_tls_input_stream_gnutls_init (GTlsInputStreamGnutls *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutlsPrivate);
+}
+
+GInputStream *
+_g_tls_input_stream_gnutls_new (GTlsConnectionGnutls *conn)
+{
+ GTlsInputStreamGnutls *tls_stream;
+
+ tls_stream = g_object_new (G_TYPE_TLS_INPUT_STREAM_GNUTLS, NULL);
+ tls_stream->priv->conn = g_object_ref (conn);
+ return G_INPUT_STREAM (tls_stream);
+}
diff --git a/gio/gnutls/gtlsinputstream-gnutls.h b/gio/gnutls/gtlsinputstream-gnutls.h
new file mode 100644
index 0000000..80ae0d4
--- /dev/null
+++ b/gio/gnutls/gtlsinputstream-gnutls.h
@@ -0,0 +1,48 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_INPUT_STREAM_GNUTLS_H__
+#define __G_TLS_INPUT_STREAM_GNUTLS_H__
+
+#include <gio/ginputstream.h>
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_INPUT_STREAM_GNUTLS (_g_tls_input_stream_gnutls_get_type ())
+#define G_TLS_INPUT_STREAM_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutls))
+#define G_TLS_INPUT_STREAM_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutlsClass))
+#define G_IS_TLS_INPUT_STREAM_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_INPUT_STREAM_GNUTLS))
+#define G_IS_TLS_INPUT_STREAM_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_INPUT_STREAM_GNUTLS))
+#define G_TLS_INPUT_STREAM_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_INPUT_STREAM_GNUTLS, GTlsInputStreamGnutlsClass))
+
+typedef struct _GTlsInputStreamGnutlsPrivate GTlsInputStreamGnutlsPrivate;
+typedef struct _GTlsInputStreamGnutlsClass GTlsInputStreamGnutlsClass;
+typedef struct _GTlsInputStreamGnutls GTlsInputStreamGnutls;
+
+struct _GTlsInputStreamGnutlsClass
+{
+ GInputStreamClass parent_class;
+};
+
+struct _GTlsInputStreamGnutls
+{
+ GInputStream parent_instance;
+ GTlsInputStreamGnutlsPrivate *priv;
+};
+
+GType _g_tls_input_stream_gnutls_get_type (void) G_GNUC_CONST;
+GInputStream *_g_tls_input_stream_gnutls_new (GTlsConnectionGnutls *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_INPUT_STREAM_GNUTLS_H___ */
diff --git a/gio/gnutls/gtlsoutputstream-gnutls.c b/gio/gnutls/gtlsoutputstream-gnutls.c
new file mode 100644
index 0000000..1218c31
--- /dev/null
+++ b/gio/gnutls/gtlsoutputstream-gnutls.c
@@ -0,0 +1,240 @@
+/* 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 "gtlsoutputstream-gnutls.h"
+#include "gasyncresult.h"
+#include "gcancellable.h"
+#include "gioerror.h"
+#include "gpollableoutputstream.h"
+#include "gsimpleasyncresult.h"
+#include "gtlsconnection-gnutls.h"
+#include "glibintl.h"
+
+static void g_tls_output_stream_gnutls_pollable_iface_init (GPollableOutputStreamInterface *iface);
+
+#define g_tls_output_stream_gnutls_get_type _g_tls_output_stream_gnutls_get_type
+G_DEFINE_TYPE_WITH_CODE (GTlsOutputStreamGnutls, g_tls_output_stream_gnutls, G_TYPE_OUTPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, g_tls_output_stream_gnutls_pollable_iface_init)
+ )
+
+struct _GTlsOutputStreamGnutlsPrivate
+{
+ GTlsConnectionGnutls *conn;
+
+ /* pending operation metadata */
+ GCancellable *cancellable;
+ gconstpointer buffer;
+ gsize count;
+};
+
+static void
+g_tls_output_stream_gnutls_finalize (GObject *object)
+{
+ GTlsOutputStreamGnutls *stream = G_TLS_OUTPUT_STREAM_GNUTLS (object);
+
+ if (stream->priv->conn)
+ g_object_unref (stream->priv->conn);
+
+ G_OBJECT_CLASS (g_tls_output_stream_gnutls_parent_class)->finalize (object);
+}
+
+static gssize
+g_tls_output_stream_gnutls_write (GOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
+
+ return _g_tls_connection_gnutls_write (tls_stream->priv->conn,
+ buffer, count, TRUE,
+ cancellable, error);
+}
+
+static gboolean
+g_tls_output_stream_gnutls_write_ready (GIOStreamAdapter *adapter,
+ gpointer user_data)
+{
+ GTlsOutputStreamGnutls *tls_stream;
+ GSimpleAsyncResult *simple = user_data;
+ gssize nwrote;
+ GError *error = NULL;
+
+ tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ g_object_unref (tls_stream);
+
+ nwrote = _g_tls_connection_gnutls_write (tls_stream->priv->conn,
+ tls_stream->priv->buffer,
+ tls_stream->priv->count, FALSE,
+ tls_stream->priv->cancellable,
+ &error);
+ if (nwrote == -1 &&
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ g_error_free (error);
+ return TRUE;
+ }
+
+ if (error)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+ else
+ g_simple_async_result_set_op_res_gssize (simple, nwrote);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+
+ return FALSE;
+}
+
+static void
+g_tls_output_stream_gnutls_write_async (GOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
+ GSimpleAsyncResult *simple;
+ gssize nwrote;
+ GError *error = NULL;
+ GSource *source;
+
+ simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+ g_tls_output_stream_gnutls_write_async);
+ nwrote = _g_tls_connection_gnutls_write (tls_stream->priv->conn,
+ buffer, count, FALSE,
+ cancellable, &error);
+
+ if (nwrote >= 0 ||
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ if (error)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+ else
+ g_simple_async_result_set_op_res_gssize (simple, nwrote);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ tls_stream->priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ tls_stream->priv->buffer = buffer;
+ tls_stream->priv->count = count;
+
+ source = _g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+ G_IO_OUT,
+ tls_stream->priv->cancellable);
+ g_source_set_callback (source,
+ (GSourceFunc) g_tls_output_stream_gnutls_write_ready,
+ simple, NULL);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+}
+
+static gssize
+g_tls_output_stream_gnutls_write_finish (GOutputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_TLS_OUTPUT_STREAM_GNUTLS (stream), -1);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_output_stream_gnutls_write_async), -1);
+
+ return g_simple_async_result_get_op_res_gssize (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+static gboolean
+g_tls_output_stream_gnutls_pollable_is_writable (GPollableOutputStream *pollable)
+{
+ GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+
+ return _g_tls_connection_gnutls_check (tls_stream->priv->conn, G_IO_OUT);
+}
+
+static GSource *
+g_tls_output_stream_gnutls_pollable_create_source (GPollableOutputStream *pollable,
+ GCancellable *cancellable)
+{
+ GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+
+ return _g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
+ G_IO_OUT,
+ cancellable);
+}
+
+static gssize
+g_tls_output_stream_gnutls_pollable_write_nonblocking (GPollableOutputStream *pollable,
+ const void *buffer,
+ gsize size,
+ GError **error)
+{
+ GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+
+ return _g_tls_connection_gnutls_write (tls_stream->priv->conn,
+ buffer, size, FALSE,
+ NULL, error);
+}
+
+static void
+g_tls_output_stream_gnutls_class_init (GTlsOutputStreamGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsOutputStreamGnutlsPrivate));
+
+ gobject_class->finalize = g_tls_output_stream_gnutls_finalize;
+
+ output_stream_class->write_fn = g_tls_output_stream_gnutls_write;
+ output_stream_class->write_async = g_tls_output_stream_gnutls_write_async;
+ output_stream_class->write_finish = g_tls_output_stream_gnutls_write_finish;
+}
+
+static void
+g_tls_output_stream_gnutls_pollable_iface_init (GPollableOutputStreamInterface *iface)
+{
+ iface->is_writable = g_tls_output_stream_gnutls_pollable_is_writable;
+ iface->create_source = g_tls_output_stream_gnutls_pollable_create_source;
+ iface->write_nonblocking = g_tls_output_stream_gnutls_pollable_write_nonblocking;
+}
+
+static void
+g_tls_output_stream_gnutls_init (GTlsOutputStreamGnutls *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsPrivate);
+}
+
+GOutputStream *
+_g_tls_output_stream_gnutls_new (GTlsConnectionGnutls *conn)
+{
+ GTlsOutputStreamGnutls *tls_stream;
+
+ tls_stream = g_object_new (G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, NULL);
+ tls_stream->priv->conn = g_object_ref (conn);
+ return G_OUTPUT_STREAM (tls_stream);
+}
diff --git a/gio/gnutls/gtlsoutputstream-gnutls.h b/gio/gnutls/gtlsoutputstream-gnutls.h
new file mode 100644
index 0000000..4a074cd
--- /dev/null
+++ b/gio/gnutls/gtlsoutputstream-gnutls.h
@@ -0,0 +1,48 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_OUTPUT_STREAM_GNUTLS_H__
+#define __G_TLS_OUTPUT_STREAM_GNUTLS_H__
+
+#include <gio/goutputstream.h>
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OUTPUT_STREAM_GNUTLS (_g_tls_output_stream_gnutls_get_type ())
+#define G_TLS_OUTPUT_STREAM_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutls))
+#define G_TLS_OUTPUT_STREAM_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsClass))
+#define G_IS_TLS_OUTPUT_STREAM_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS))
+#define G_IS_TLS_OUTPUT_STREAM_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS))
+#define G_TLS_OUTPUT_STREAM_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsClass))
+
+typedef struct _GTlsOutputStreamGnutlsPrivate GTlsOutputStreamGnutlsPrivate;
+typedef struct _GTlsOutputStreamGnutlsClass GTlsOutputStreamGnutlsClass;
+typedef struct _GTlsOutputStreamGnutls GTlsOutputStreamGnutls;
+
+struct _GTlsOutputStreamGnutlsClass
+{
+ GOutputStreamClass parent_class;
+};
+
+struct _GTlsOutputStreamGnutls
+{
+ GOutputStream parent_instance;
+ GTlsOutputStreamGnutlsPrivate *priv;
+};
+
+GType _g_tls_output_stream_gnutls_get_type (void) G_GNUC_CONST;
+GOutputStream *_g_tls_output_stream_gnutls_new (GTlsConnectionGnutls *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_OUTPUT_STREAM_GNUTLS_H___ */
diff --git a/gio/gnutls/gtlsserverconnection-gnutls.c b/gio/gnutls/gtlsserverconnection-gnutls.c
new file mode 100644
index 0000000..ce61a90
--- /dev/null
+++ b/gio/gnutls/gtlsserverconnection-gnutls.c
@@ -0,0 +1,196 @@
+/* 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 <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "gtlsserverconnection-gnutls.h"
+#include "ginitable.h"
+#include "gioenumtypes.h"
+#include "gtlscertificate-gnutls.h"
+#include "glibintl.h"
+
+enum
+{
+ PROP_0,
+ PROP_AUTHENTICATION_MODE
+};
+
+static void g_tls_server_connection_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void g_tls_server_connection_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn);
+static gboolean g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls *conn,
+ gboolean success,
+ GError **inout_error);
+
+static void g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface);
+
+static int g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t session,
+ gnutls_retr_st *st);
+
+#define g_tls_server_connection_gnutls_get_type _g_tls_server_connection_gnutls_get_type
+G_DEFINE_TYPE_WITH_CODE (GTlsServerConnectionGnutls, g_tls_server_connection_gnutls, G_TYPE_TLS_CONNECTION_GNUTLS,
+ G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION,
+ g_tls_server_connection_gnutls_server_connection_interface_init))
+
+struct _GTlsServerConnectionGnutlsPrivate
+{
+ GTlsAuthenticationMode authentication_mode;
+};
+
+static void
+g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GTlsServerConnectionGnutlsPrivate));
+
+ gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
+ gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
+
+ connection_gnutls_class->begin_handshake = g_tls_server_connection_gnutls_begin_handshake;
+ connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
+
+ g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
+}
+
+static void
+g_tls_server_connection_gnutls_server_connection_interface_init (GTlsServerConnectionInterface *iface)
+{
+}
+
+static void
+g_tls_server_connection_gnutls_init (GTlsServerConnectionGnutls *gnutls)
+{
+ gnutls_certificate_credentials_t creds;
+
+ gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsPrivate);
+
+ creds = _g_tls_connection_gnutls_get_credentials (G_TLS_CONNECTION_GNUTLS (gnutls));
+ gnutls_certificate_server_set_retrieve_function (creds, g_tls_server_connection_gnutls_retrieve_function);
+}
+
+static void
+g_tls_server_connection_gnutls_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUTHENTICATION_MODE:
+ g_value_set_enum (value, gnutls->priv->authentication_mode);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_tls_server_connection_gnutls_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUTHENTICATION_MODE:
+ gnutls->priv->authentication_mode = g_value_get_enum (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static int
+g_tls_server_connection_gnutls_retrieve_function (gnutls_session_t session,
+ gnutls_retr_st *st)
+{
+ _g_tls_connection_gnutls_get_certificate (gnutls_transport_get_ptr (session), st);
+ return 0;
+}
+
+static void
+g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+{
+ GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
+ gnutls_session_t session;
+ gnutls_certificate_request_t req_mode;
+
+ switch (gnutls->priv->authentication_mode)
+ {
+ case G_TLS_AUTHENTICATION_REQUESTED:
+ req_mode = GNUTLS_CERT_REQUEST;
+ break;
+ case G_TLS_AUTHENTICATION_REQUIRED:
+ req_mode = GNUTLS_CERT_REQUIRE;
+ break;
+ default:
+ req_mode = GNUTLS_CERT_IGNORE;
+ break;
+ }
+
+ session = _g_tls_connection_gnutls_get_session (conn);
+ gnutls_certificate_server_set_request (session, req_mode);
+}
+
+static gboolean
+g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls *gnutls,
+ gboolean success,
+ GError **inout_error)
+{
+ GTlsValidationFlags gtls_errors;
+ GTlsCertificate *peer;
+
+ if (!success)
+ return FALSE;
+
+ peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (gnutls));
+ if (peer)
+ {
+ gtls_errors = _g_tls_connection_gnutls_validate_peer (gnutls);
+ if (!g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer, gtls_errors))
+ {
+ g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Unacceptable TLS certificate"));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/gio/gnutls/gtlsserverconnection-gnutls.h b/gio/gnutls/gtlsserverconnection-gnutls.h
new file mode 100644
index 0000000..37e386a
--- /dev/null
+++ b/gio/gnutls/gtlsserverconnection-gnutls.h
@@ -0,0 +1,47 @@
+/* GIO - GLib ServerConnection, Output and Gnutlsing Library
+ *
+ * Copyright © 2010 Red Hat, Inc.
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef __G_TLS_SERVER_CONNECTION_GNUTLS_H__
+#define __G_TLS_SERVER_CONNECTION_GNUTLS_H__
+
+#include <gio/gtlsserverconnection.h>
+#include "gtlsconnection-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_SERVER_CONNECTION_GNUTLS (_g_tls_server_connection_gnutls_get_type ())
+#define G_TLS_SERVER_CONNECTION_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutls))
+#define G_TLS_SERVER_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsClass))
+#define G_IS_TLS_SERVER_CONNECTION_GNUTLS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS))
+#define G_IS_TLS_SERVER_CONNECTION_GNUTLS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS))
+#define G_TLS_SERVER_CONNECTION_GNUTLS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_SERVER_CONNECTION_GNUTLS, GTlsServerConnectionGnutlsClass))
+
+typedef struct _GTlsServerConnectionGnutlsPrivate GTlsServerConnectionGnutlsPrivate;
+typedef struct _GTlsServerConnectionGnutlsClass GTlsServerConnectionGnutlsClass;
+typedef struct _GTlsServerConnectionGnutls GTlsServerConnectionGnutls;
+
+struct _GTlsServerConnectionGnutlsClass
+{
+ GTlsConnectionGnutlsClass parent_class;
+};
+
+struct _GTlsServerConnectionGnutls
+{
+ GTlsConnectionGnutls parent_instance;
+ GTlsServerConnectionGnutlsPrivate *priv;
+};
+
+GType _g_tls_server_connection_gnutls_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __G_TLS_SERVER_CONNECTION_GNUTLS_H___ */
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index 4a1c832..56afe0e 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -44,6 +44,8 @@
#include <gio/gsocketaddress.h>
#include <gio/gtcpconnection.h>
#include <gio/gtcpwrapperconnection.h>
+#include <gio/gtlscertificate.h>
+#include <gio/gtlsclientconnection.h>
#include "glibintl.h"
@@ -79,6 +81,8 @@ enum
PROP_LOCAL_ADDRESS,
PROP_TIMEOUT,
PROP_ENABLE_PROXY,
+ PROP_TLS,
+ PROP_TLS_VALIDATION_FLAGS
};
struct _GSocketClientPrivate
@@ -90,6 +94,8 @@ struct _GSocketClientPrivate
guint timeout;
gboolean enable_proxy;
GHashTable *app_proxies;
+ gboolean tls;
+ GTlsValidationFlags tls_validation_flags;
};
static GSocket *
@@ -218,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);
}
@@ -257,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);
}
@@ -525,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.26
+ */
+GTlsValidationFlags
+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_VALIDATE_ALL.
+ *
+ * Since: 2.26
+ */
+void
+g_socket_client_set_tls_validation_flags (GSocketClient *client,
+ GTlsValidationFlags 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)
{
@@ -593,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_VALIDATION_FLAGS,
+ G_TLS_VALIDATE_ALL,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
}
/**
@@ -631,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;
@@ -686,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) &&
@@ -716,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 (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))
@@ -753,12 +862,44 @@ g_socket_client_connect (GSocketClient *client,
}
}
+ if (connection && client->priv->tls)
+ {
+ GTlsClientConnection *tlsconn;
+
+ tlsconn = g_tls_client_connection_new (&last_error,
+ "base-io-stream", connection,
+ "server-identity", connectable,
+ "validation-flags", client->priv->tls_validation_flags,
+ NULL);
+ g_object_unref (connection);
+ connection = (GIOStream *)tlsconn;
+
+ if (tlsconn)
+ {
+ 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);
}
/**
@@ -926,10 +1067,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;
@@ -945,6 +1087,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);
@@ -952,6 +1104,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);
@@ -986,45 +1139,104 @@ 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 (g_tls_connection_handshake_finish (G_TLS_CONNECTION (object),
+ result,
+ &data->last_error))
+ {
+ g_object_unref (data->connection);
+ data->connection = G_IO_STREAM (object);
+ }
+ else
+ {
+ 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 (io_stream)
+ if (!data->client->priv->tls)
{
- if (G_IS_SOCKET_CONNECTION (io_stream))
- data->connection = G_SOCKET_CONNECTION (io_stream);
- else
- {
- data->connection = g_tcp_wrapper_connection_new (io_stream,
- data->current_socket);
- g_object_unref (io_stream);
- }
+ g_socket_client_async_connect_complete (data);
+ return;
+ }
+
+ tlsconn = g_tls_client_connection_new (&data->last_error,
+ "base-io-stream", data->connection,
+ "server-identity", data->connectable,
+ "validation-flags", data->client->priv->tls_validation_flags,
+ NULL);
+ if (tlsconn)
+ {
+ 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_async_connect_complete (data);
+ 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,
@@ -1049,7 +1261,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,
@@ -1065,6 +1277,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);
@@ -1079,13 +1293,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
@@ -1242,6 +1453,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 2586f70..d0d27cb 100644
--- a/gio/gsocketclient.h
+++ b/gio/gsocketclient.h
@@ -89,6 +89,16 @@ 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);
+gboolean g_socket_client_get_tls_handshake (GSocketClient *client);
+void g_socket_client_set_tls_handshake (GSocketClient *client,
+ gboolean tls_handshake);
+GTlsValidationFlags g_socket_client_get_tls_validation_flags (GSocketClient *client);
+void g_socket_client_set_tls_validation_flags (GSocketClient *client,
+ GTlsValidationFlags 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 0000000..fa8cd3d
--- /dev/null
+++ b/gio/gtlsbackend.c
@@ -0,0 +1,157 @@
+/* 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 "gioenumtypes.h"
+#include "gnutls/gtlsbackend-gnutls.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. The returned object will
+ * be a #GTlsClientConnection, which wraps the underlying
+ * #GSocketConnection.
+ *
+ * If you need to control details of the TLS handshake (eg, to allow
+ * certain bad certificates to be accepted), you can set
+ * #GSocketClient:tls-handshake to %FALSE on the client, and then it
+ * will return un-handshaked connections, so you can connect to
+ * signals such as #GTlsConnection::accept-certificate on the
+ * connection before starting the handshake.
+ *
+ * You can also create a #GTlsClientConnection by hand, wrapping an
+ * arbitrary #GPollableIOStream.
+ *
+ * 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
+ * @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)
+{
+ /* FIXME */
+ return g_object_new (G_TYPE_TLS_BACKEND_GNUTLS, 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_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 0000000..5a16742
--- /dev/null
+++ b/gio/gtlsbackend.h
@@ -0,0 +1,76 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_BACKEND_H__
+#define __G_TLS_BACKEND_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+/**
+ * G_TLS_EXTENSION_POINT_NAME:
+ *
+ * Extension point for TLS functionality via #GTlsBackend.
+ * See <link linkend="extending-gio">Extending GIO</link>.
+ */
+#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.
+ */
+
+typedef struct _GTlsBackend GTlsBackend;
+typedef struct _GTlsBackendInterface GTlsBackendInterface;
+
+struct _GTlsBackendInterface
+{
+ GTypeInterface g_iface;
+
+ /* methods */
+ 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);
+
+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 0000000..d154cd2
--- /dev/null
+++ b/gio/gtlscertificate.c
@@ -0,0 +1,508 @@
+/* GIO - GLib Input, Output and Certificateing Library
+ *
+ * Copyright (C) 2009 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 <string.h>
+#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));
+}
+
+/**
+ * g_tls_certificate_new:
+ * @error: #GError for error reporting, or %NULL to ignore.
+ * @...: a %NULL-terminated list of property values and names
+ *
+ * Creates a new #GTlsCertificate based on the given properties. This
+ * is a wrapper around g_initable_new() that looks up the correct type
+ * from the active #GTlsBackend.
+ *
+ * Return value: the new #GTlsCertificate, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_certificate_new (GError **error,
+ ...)
+{
+ GTlsCertificate *cert;
+ GTlsBackend *backend;
+ const gchar *first_property_name;
+ va_list ap;
+
+ backend = g_tls_backend_get_default ();
+
+ va_start (ap, error);
+ first_property_name = va_arg (ap, const gchar *);
+ cert = (GTlsCertificate *) g_initable_new_valist (g_tls_backend_get_certificate_type (backend),
+ first_property_name, ap,
+ NULL, error);
+ va_end (ap);
+
+ return 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 (error,
+ "certificate-pem", cert_pem,
+ "private-key-pem", privkey_pem,
+ NULL);
+ 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 (error,
+ "certificate-pem", cert_data,
+ "private-key-pem", key_data,
+ NULL);
+ 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 #GTlsCertificate<!-- -->s 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 0000000..4abed4c
--- /dev/null
+++ b/gio/gtlscertificate.h
@@ -0,0 +1,78 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_CERTIFICATE_H__
+#define __G_TLS_CERTIFICATE_H__
+
+#include <gio/giotypes.h>
+
+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 (GError **error,
+ ...) G_GNUC_NULL_TERMINATED;
+
+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 0000000..24831fc
--- /dev/null
+++ b/gio/gtlsclientconnection.c
@@ -0,0 +1,337 @@
+/* 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 #GTlsClientConnection::accept-certificate.
+ *
+ * Note that if you do not override this property at construct time,
+ * the initial value will actually be set from the connection's
+ * #GTlsClientContext.
+ *
+ * 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_VALIDATION_FLAGS,
+ G_TLS_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_VALIDATE_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_VALIDATE_IDENTITY validation will fail.
+ *
+ * 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
+ * negotiate 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::get_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:
+ * @error: #GError for error reporting, or %NULL to ignore.
+ * @...: a %NULL-terminated list of property values and names
+ *
+ * Creates a new #GTlsClientConnection based on the #GIOstream
+ * specified as "base-io-stream" in the property list. This is a
+ * wrapper around g_initable_new() that looks up the correct type from
+ * the active #GTlsBackend.
+ *
+ * Return value: the new #GTlsClientConnection, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsClientConnection *
+g_tls_client_connection_new (GError **error,
+ ...)
+{
+ GObject *conn;
+ GTlsBackend *backend;
+ const gchar *first_property_name;
+ va_list ap;
+
+ va_start (ap, error);
+ first_property_name = va_arg (ap, const gchar *);
+ /* There must be at least a "base-io-stream" property, since that's
+ * required and construct-only.
+ */
+ g_return_val_if_fail (first_property_name != NULL, NULL);
+
+ backend = g_tls_backend_get_default ();
+ conn = g_initable_new_valist (g_tls_backend_get_client_connection_type (backend),
+ first_property_name, ap,
+ NULL, error);
+ va_end (ap);
+
+ 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
+ */
+GTlsValidationFlags
+g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn)
+{
+ GTlsValidationFlags 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 #GTlsValidationFlags 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,
+ GTlsValidationFlags 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 for the
+ * %G_TLS_VALIDATE_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 0000000..b7389d5
--- /dev/null
+++ b/gio/gtlsclientconnection.h
@@ -0,0 +1,71 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_CLIENT_CONNECTION_H__
+#define __G_TLS_CLIENT_CONNECTION_H__
+
+#include <gio/gtlsconnection.h>
+
+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 (GError **error,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GTlsValidationFlags g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn);
+void g_tls_client_connection_set_validation_flags (GTlsClientConnection *conn,
+ GTlsValidationFlags 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 0000000..1792aca
--- /dev/null
+++ b/gio/gtlsconnection.c
@@ -0,0 +1,741 @@
+/* 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 "gpollableiostream.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_CA_LIST,
+ 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_POLLABLE_IO_STREAM,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * GTlsConnection:ca-list:
+ *
+ * The list of trusted Certificate Authorities for authenticating
+ * peer certificates. This will be initialized with a default
+ * list of CAs trusted by the system.
+ *
+ * Since: 2.28
+ */
+ g_object_class_install_property (gobject_class, PROP_CA_LIST,
+ g_param_spec_pointer ("ca-list",
+ P_("CA List"),
+ P_("The list of trusted Certificate Authorities"),
+ G_PARAM_READWRITE |
+ 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: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_VALIDATION_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_get_ca_list:
+ * @connection: the #GTlsConnection
+ *
+ * Gets @connection's trusted Certificate Authority list. See
+ * g_tls_connection_set_ca_list() for more details.
+ *
+ * Return value: (element-type Gio.TlsCertificate) (transfer full):
+ * the list of CA certificates.
+ *
+ * Since: 2.28
+ */
+GList *
+g_tls_connection_get_ca_list (GTlsConnection *conn)
+{
+ GList *ca_list = NULL;
+
+ g_object_get (G_OBJECT (conn), "ca-list", &ca_list, NULL);
+ return ca_list;
+}
+
+/**
+ * g_tls_connection_set_ca_list:
+ * @connection: the #GTlsConnection
+ * @ca_list: (transfer full) (element-type Gio.TlsCertificate): a list
+ * of #GTlsCertificates
+ *
+ * Sets @connection's trusted Certificate Authority list to the provided
+ * list of #GTlsCertificate.
+ *
+ * You should not normally need to call this; a #GTlsConnection will be
+ * initialized to use a default list of CAs provided by the system.
+ * However, this could be used if you wish to trust only a more
+ * restricted set of CAs, or (in conjuction with
+ * g_tls_connection_get_ca_list()) if you need to add a CA to the list.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_connection_set_ca_list (GTlsConnection *conn,
+ GList *ca_list)
+{
+ g_object_set (G_OBJECT (conn), "ca-list", ca_list, NULL);
+}
+
+/**
+ * g_tls_connection_set_ca_list_from_file:
+ * @connection: the #GTlsConnection
+ * @ca_file: the name of a file containing PEM-encoded certificates
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Sets @connection's trusted Certificate Authority list by parsing the
+ * contents of @ca_file. As with g_tls_connection_set_ca_list(), this can
+ * be used to override the default set of CAs.
+ *
+ * If @file cannot be read or parsed, @connection's CA list will be
+ * unchanged, and @error will be set.
+ *
+ * Return value: success or failure
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_connection_set_ca_list_from_file (GTlsConnection *conn,
+ const char *ca_file,
+ GError **error)
+{
+ GList *ca_list, *l;
+ GError *my_error = NULL;
+
+ ca_list = g_tls_certificate_list_new_from_file (ca_file, &my_error);
+ if (my_error)
+ {
+ g_propagate_error (error, my_error);
+ return FALSE;
+ }
+
+ g_tls_connection_set_ca_list (conn, ca_list);
+ for (l = ca_list; l; l = l->next)
+ g_object_unref (l->data);
+ g_list_free (ca_list);
+ return TRUE;
+}
+
+/**
+ * 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_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 <link linkend="io-priority">I/O priority</link>
+ * 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,
+ GTlsValidationFlags 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 0000000..728fdab
--- /dev/null
+++ b/gio/gtlsconnection.h
@@ -0,0 +1,141 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_CONNECTION_H__
+#define __G_TLS_CONNECTION_H__
+
+#include <gio/giostream.h>
+
+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,
+ GTlsValidationFlags 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;
+
+GList * g_tls_connection_get_ca_list (GTlsConnection *conn);
+void g_tls_connection_set_ca_list (GTlsConnection *conn,
+ GList *ca_list);
+gboolean g_tls_connection_set_ca_list_from_file (GTlsConnection *conn,
+ const char *ca_file,
+ GError **error);
+
+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);
+
+
+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,
+ GTlsValidationFlags 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 0000000..b7d555b
--- /dev/null
+++ b/gio/gtlsserverconnection.c
@@ -0,0 +1,105 @@
+/* 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:
+ * @error: #GError for error reporting, or %NULL to ignore.
+ * @...: a %NULL-terminated list of property values and names
+ *
+ * Creates a new #GTlsServerConnection based on the #GIOstream
+ * specified as "base-io-stream" in the property list. This is a
+ * wrapper around g_initable_new() that looks up the correct type from
+ * the active #GTlsBackend.
+ *
+ * Return value: the new #GTlsServerConnection, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsServerConnection *
+g_tls_server_connection_new (GError **error,
+ ...)
+{
+ GObject *conn;
+ GTlsBackend *backend;
+ const gchar *first_property_name;
+ va_list ap;
+
+ va_start (ap, error);
+ first_property_name = va_arg (ap, const gchar *);
+ /* There must be at least a "base-io-stream" property, since that's
+ * required and construct-only.
+ */
+ g_return_val_if_fail (first_property_name != NULL, NULL);
+
+ backend = g_tls_backend_get_default ();
+ conn = g_initable_new_valist (g_tls_backend_get_server_connection_type (backend),
+ first_property_name, ap,
+ NULL, error);
+ va_end (ap);
+
+ return G_TLS_SERVER_CONNECTION (conn);
+}
diff --git a/gio/gtlsserverconnection.h b/gio/gtlsserverconnection.h
new file mode 100644
index 0000000..07e33cd
--- /dev/null
+++ b/gio/gtlsserverconnection.h
@@ -0,0 +1,60 @@
+/* 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 <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_SERVER_CONNECTION_H__
+#define __G_TLS_SERVER_CONNECTION_H__
+
+#include <gio/gtlsconnection.h>
+
+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 (GError **error,
+ ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* __G_TLS_SERVER_CONNECTION_H__ */
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index 13d9403..bdc704d 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -68,6 +68,7 @@ socket-client
socket-server
srvtarget
test.mo
+tls-test
unix-fd
unix-streams
volumemonitor
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index ad2273a..19b9ece 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -64,6 +64,7 @@ TEST_PROGS += \
appinfo \
contenttype \
file \
+ tls-test \
$(NULL)
endif
@@ -92,7 +93,6 @@ SAMPLE_PROGS = \
gapplication-example-actions \
$(NULL)
-
if OS_UNIX
TEST_PROGS += live-g-file desktop-app-info unix-fd unix-streams gapplication
SAMPLE_PROGS += gdbus-example-unix-fd-client
@@ -403,6 +403,11 @@ proxy_SOURCES = proxy.c
proxy_LDADD = $(progs_ldadd) \
$(top_builddir)/gthread/libgthread-2.0.la
+tls_test_SOURCES = tls-test.c
+tls_test_LDADD = $(progs_ldadd) \
+ $(top_builddir)/gthread/libgthread-2.0.la \
+ $(LIBGNUTLS_LIBS) $(LIBGCRYPT_LIBS)
+
EXTRA_DIST += \
socket-common.c \
org.gtk.test.gschema \
@@ -417,7 +422,7 @@ EXTRA_DIST += \
appinfo-test-notgnome.desktop \
gdbus-testserver.py
-MISC_STUFF = test.mo
+MISC_STUFF = gschemas.compiled test.mo
test.mo: de.po
$(MSGFMT) -o test.mo $(srcdir)/de.po; \
diff --git a/gio/tests/socket-client.c b/gio/tests/socket-client.c
index 1f1455e..52f1392 100644
--- a/gio/tests/socket-client.c
+++ b/gio/tests/socket-client.c
@@ -5,17 +5,15 @@
#include <stdio.h>
#include <string.h>
-#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}
};
-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);
- }
- }
-}
+#include "socket-common.c"
-static gpointer
-cancel_thread (gpointer data)
+static gboolean
+accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert,
+ GTlsValidationFlags errors, gpointer user_data)
{
- GCancellable *cancellable = data;
-
- g_usleep (1000*1000*cancel_timeout);
- g_print ("Cancelling\n");
- g_cancellable_cancel (cancellable);
- return NULL;
+ g_print ("Certificate would have been rejected ( ");
+ if (errors & G_TLS_VALIDATE_CA)
+ g_print ("unknown-ca ");
+ if (errors & G_TLS_VALIDATE_IDENTITY)
+ g_print ("identity-mismatch ");
+ if (errors & G_TLS_VALIDATE_ACTIVATION)
+ g_print ("not-activated ");
+ if (errors & G_TLS_VALIDATE_EXPIRATION)
+ g_print ("expired ");
+ if (errors & G_TLS_VALIDATE_REVOCATION)
+ g_print ("revoked ");
+ if (errors & G_TLS_VALIDATE_ALGORITHM)
+ g_print ("bad-algorithm ");
+ g_print (") but accepting anyway.\n");
+
+ 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,52 @@ 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 (&error,
+ "base-io-stream", connection,
+ "server-identity", connectable,
+ NULL);
+ 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 +250,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 +295,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 +335,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 8d2933b..fdee383 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 562f1ef..0bf8c33 100644
--- a/gio/tests/socket-server.c
+++ b/gio/tests/socket-server.c
@@ -4,8 +4,6 @@
#include <stdlib.h>
#include <string.h>
-#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,48 @@ 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 (&error,
+ "base-io-stream", connection,
+ "certificate", tlscert,
+ NULL);
+ 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 +248,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 +295,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 +342,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 +359,7 @@ main (int argc,
error->message);
return 1;
}
-
- g_object_unref (G_OBJECT (socket));
+ g_object_unref (socket);
return 0;
}
diff --git a/gio/tests/test-cert.pem b/gio/tests/test-cert.pem
new file mode 100644
index 0000000..09c87b0
--- /dev/null
+++ b/gio/tests/test-cert.pem
@@ -0,0 +1,37 @@
+-----BEGIN CERTIFICATE-----
+MIIDjzCCAvigAwIBAgIBADANBgkqhkiG9w0BAQQFADCBkjELMAkGA1UEBhMCVVMx
+FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcTBkJvc3RvbjEPMA0GA1UE
+ChMGWGltaWFuMRUwEwYDVQQLEwxTb3VwIEtpdGNoZW4xEjAQBgNVBAMTCWxvY2Fs
+aG9zdDEeMBwGCSqGSIb3DQEJARYPc291cEB4aW1pYW4uY29tMB4XDTAzMDkyMzE4
+Mzc0MVoXDTEzMDkyMzE4Mzc0MVowgZIxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1N
+YXNzYWNodXNldHRzMQ8wDQYDVQQHEwZCb3N0b24xDzANBgNVBAoTBlhpbWlhbjEV
+MBMGA1UECxMMU291cCBLaXRjaGVuMRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkq
+hkiG9w0BCQEWD3NvdXBAeGltaWFuLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAwzT/WxfdXqb2hbyjQav3FtN7tLxj3UbZKCKDYlizBsNLxb9exfebhV4h
+CoAcaSNvLUnk3tAXnk+BDsIC1V4SbwqHYR17PnO3YZ8fkNwh5RGZwNx+zafdfFyu
++3Sh+mE03bljpDlTsgPL8CiFCd68MPRnuHoKt5iTpSyLC6Df0qcCAwEAAaOB8jCB
+7zAdBgNVHQ4EFgQU9A9omrgBK5Kkl6FRxrgJU2voj4Uwgb8GA1UdIwSBtzCBtIAU
+9A9omrgBK5Kkl6FRxrgJU2voj4WhgZikgZUwgZIxCzAJBgNVBAYTAlVTMRYwFAYD
+VQQIEw1NYXNzYWNodXNldHRzMQ8wDQYDVQQHEwZCb3N0b24xDzANBgNVBAoTBlhp
+bWlhbjEVMBMGA1UECxMMU291cCBLaXRjaGVuMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
+HjAcBgkqhkiG9w0BCQEWD3NvdXBAeGltaWFuLmNvbYIBADAMBgNVHRMEBTADAQH/
+MA0GCSqGSIb3DQEBBAUAA4GBAGCV56N7bEDNdE76T8i68gS00NIVVosVQjS39Ojd
+ED+rvq0YYvuc2UXlzAonuCJfwFc73g4wSIjS0xijF5rnugZ+aay0LNv2y+Rf34CQ
+RNswrwurFjlxgTOO+Wx2IM64mAnBfj43M8uKEZFqAiGKrZZ0xIqyUMlku0FgXDH2
+Jvpg
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDDNP9bF91epvaFvKNBq/cW03u0vGPdRtkoIoNiWLMGw0vFv17F
+95uFXiEKgBxpI28tSeTe0BeeT4EOwgLVXhJvCodhHXs+c7dhnx+Q3CHlEZnA3H7N
+p918XK77dKH6YTTduWOkOVOyA8vwKIUJ3rww9Ge4egq3mJOlLIsLoN/SpwIDAQAB
+AoGAOGAi6zzuKrrPcXo0L/ApEQeMr3rE4I/ogUXOaeWx9l8KkBafmU7UNGUl57Fu
+AxM/tXWkypCQcaEGZau0Q8jCS5wKgynNi72F4OzBqgjgW4vvtrjfC1LagnCd2ZMX
+V5XVECjO/sEDg0hJeOsXlKbECAgvHMU3dSCGO7DmuG9tIxkCQQDsth1VvVjOdfp6
+klOfYzbAM1p9HIcNPJMeuBFqq//UHX4aPqh/6G6W06TOTN+bjZBmitG9yjV958t2
+rPxl64f7AkEA0x0WOLm5S0LNsv7zwjXuTcj+NCHL36b3dK90oxX8Gq69PANL/EJY
+ItpHNLgzzo4DRmQy8q0WZlC9HYk1YljERQJAEN7+AkFnlfeErb3GJgMNQO+oEGi7
+G29o0PSvkRnHNxgPB9HVcqBfWXKmOWnzOgQB+b0FK/DAlUOzFbdImf8KhwJAFLty
+hzeV/tIcqUtoXNY3BOSMMkpvXxNikc75QVrTWzt10gLw32EUjreo7oB4dfx0TeFh
+L3vYC0w6hkAHQhU9kQJAPSEQ+Bqzlk6BrQNrNFEVzi1Rwpz7LOzhOjuYW6bsiAdX
+axA4r6Xh25x08ZU7cqX7gwVLHL6pgrEKuUs0Nc5Klg==
+-----END RSA PRIVATE KEY-----
diff --git a/gio/tests/tls-test.c b/gio/tests/tls-test.c
new file mode 100644
index 0000000..853e1dd
--- /dev/null
+++ b/gio/tests/tls-test.c
@@ -0,0 +1,340 @@
+#include <gnutls/gnutls.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gio/gio.h>
+
+#define BUFSIZE 1024
+
+static GMainLoop *loop;
+
+/* SERVER */
+
+/* Read @bufsize bytes into @buf from @session. */
+static void
+server_read (GTlsConnection *conn, char *buf, int bufsize)
+{
+ int total, nread;
+ GError *error = NULL;
+ GInputStream *stream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+
+ total = 0;
+ while (total < bufsize)
+ {
+ nread = g_input_stream_read (stream, buf + total,
+ bufsize - total, NULL,
+ &error);
+ if (nread <= 0)
+ {
+ g_error ("server read failed at position %d: %s", total,
+ error->message);
+ }
+ total += nread;
+ }
+}
+
+/* Write @bufsize bytes from @buf to @session, forcing 3 rehandshakes
+ * along the way. (We do an odd number of rehandshakes to make sure
+ * they occur at weird times relative to the client's read buffer
+ * size.)
+ */
+static void
+server_write (GTlsConnection *conn, char *buf, int bufsize)
+{
+ int total, nwrote;
+ int next_rehandshake = bufsize / 3;
+ GError *error = NULL;
+ GOutputStream *stream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+
+ total = 0;
+ while (total < bufsize)
+ {
+ if (total >= next_rehandshake)
+ {
+ if (!g_tls_connection_handshake (conn, NULL, &error))
+ {
+ g_error ("server rehandshake failed at position %d: %s",
+ total, error->message);
+ }
+ next_rehandshake = MIN (bufsize, next_rehandshake + bufsize / 3);
+ }
+
+ nwrote = g_output_stream_write (stream, buf + total,
+ next_rehandshake - total,
+ NULL, &error);
+ if (nwrote <= 0)
+ {
+ g_error ("server write failed at position %d: %s", total,
+ error->message);
+ }
+ total += nwrote;
+ }
+}
+
+static const char *ssl_cert_file = SRCDIR G_DIR_SEPARATOR_S "test-cert.pem";
+
+static gpointer
+server_thread (gpointer user_data)
+{
+ GSocket *listener = user_data, *socket;
+ GSocketConnection *conn;
+ GTlsServerConnection *tls_server;
+ GTlsConnection *tls_conn;
+ GTlsCertificate *server_cert;
+ char buf[BUFSIZE];
+ GError *error = NULL;
+
+ /* Wait for client thread to connect */
+ g_socket_set_blocking (listener, TRUE);
+ socket = g_socket_accept (listener, NULL, &error);
+ if (!socket)
+ g_error ("Could not accept on listening socket: %s", error->message);
+
+ conn = g_socket_connection_factory_create_connection (socket);
+ g_object_unref (socket);
+
+ server_cert = g_tls_certificate_new_from_file (ssl_cert_file, &error);
+ if (!server_cert)
+ g_error ("Could not read TLS certificate file: %s", error->message);
+
+ tls_server = g_tls_server_connection_new (&error,
+ "base-io-stream", conn,
+ "certificate", server_cert,
+ NULL);
+ if (!tls_server)
+ g_error ("Could not create TLS connection: %s", error->message);
+ g_object_unref (conn);
+ g_object_unref (server_cert);
+ tls_conn = G_TLS_CONNECTION (tls_server);
+
+ /* Initial handshake */
+ if (!g_tls_connection_handshake (tls_conn, NULL, &error))
+ g_error ("initial handshake failed: %s", error->message);
+
+ /* Synchronous client test. */
+ server_read (tls_conn, buf, BUFSIZE);
+ server_write (tls_conn, buf, BUFSIZE);
+
+ /* Async client test. */
+ server_read (tls_conn, buf, BUFSIZE);
+ server_write (tls_conn, buf, BUFSIZE);
+
+ /* That's all, folks. */
+ if (!g_io_stream_close (G_IO_STREAM (tls_conn), NULL, &error))
+ g_error ("TLS close failed: %s", error->message);
+
+ g_object_unref (tls_conn);
+
+ return NULL;
+}
+
+/* async client code */
+
+typedef struct {
+ GSocketConnection *conn;
+ char writebuf[BUFSIZE], readbuf[BUFSIZE];
+ int total;
+} AsyncData;
+
+static void async_read (AsyncData *data);
+static void async_write (AsyncData *data);
+
+static void
+async_read_ready (GObject *istream, GAsyncResult *result, gpointer user_data)
+{
+ AsyncData *data = user_data;
+ gsize n;
+ GError *error = NULL;
+
+ n = g_input_stream_read_finish (G_INPUT_STREAM (istream), result, &error);
+ g_assert_no_error (error);
+ data->total += n;
+
+ if (data->total < BUFSIZE)
+ {
+ async_read (data);
+ return;
+ }
+
+ if (memcmp (data->writebuf, data->readbuf, BUFSIZE) != 0)
+ g_error ("Async read didn't match write");
+
+ g_free (data);
+ g_main_loop_quit (loop);
+}
+
+static void
+async_read (AsyncData *data)
+{
+ GInputStream *istream;
+
+ istream = g_io_stream_get_input_stream (G_IO_STREAM (data->conn));
+ g_input_stream_read_async (istream, data->readbuf + data->total,
+ BUFSIZE - data->total, G_PRIORITY_DEFAULT, NULL,
+ async_read_ready, data);
+}
+
+static void
+async_write_ready (GObject *ostream, GAsyncResult *result, gpointer user_data)
+{
+ AsyncData *data = user_data;
+ gsize n;
+ GError *error = NULL;
+
+ n = g_output_stream_write_finish (G_OUTPUT_STREAM (ostream), result, &error);
+ g_assert_no_error (error);
+ data->total += n;
+
+ if (data->total < BUFSIZE)
+ {
+ async_write (data);
+ return;
+ }
+
+ data->total = 0;
+ async_read (data);
+}
+
+static void
+async_write (AsyncData *data)
+{
+ GOutputStream *ostream;
+
+ ostream = g_io_stream_get_output_stream (G_IO_STREAM (data->conn));
+ g_output_stream_write_async (ostream, data->writebuf + data->total,
+ BUFSIZE - data->total, G_PRIORITY_DEFAULT, NULL,
+ async_write_ready, data);
+}
+
+static gboolean
+start_writing (gpointer user_data)
+{
+ GSocketConnection *conn = user_data;
+ AsyncData *data;
+ int i;
+
+ data = g_new (AsyncData, 1);
+ data->conn = conn;
+ for (i = 0; i < BUFSIZE; i++)
+ data->writebuf[i] = i & 0xFF;
+ data->total = 0;
+
+ async_write (data);
+ return FALSE;
+}
+
+static GSocketConnection *conn;
+static GOutputStream *ostream;
+static GInputStream *istream;
+static GThread *server;
+
+static void
+tls_test_setup (void)
+{
+ int port;
+ GSocket *listener;
+ GInetAddress *iaddr;
+ GSocketAddress *saddr;
+ GSocketClient *client;
+ GError *error = NULL;
+
+ /* Create server socket */
+ listener = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, 0, &error);
+ if (!listener)
+ g_error ("Could not create listening socket: %s\n", error->message);
+
+ iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
+ saddr = g_inet_socket_address_new (iaddr, 0);
+ g_object_unref (iaddr);
+
+ if (!g_socket_bind (listener, saddr, TRUE, &error))
+ g_error ("Could not bind listening socket: %s\n", error->message);
+ g_object_unref (saddr);
+
+ saddr = g_socket_get_local_address (listener, NULL);
+ port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (saddr));
+ g_object_unref (saddr);
+
+ if (!g_socket_listen (listener, &error))
+ g_error ("Could not listen on socket: %s\n", error->message);
+
+ /* Now spawn server thread */
+ server = g_thread_create (server_thread, listener, TRUE, NULL);
+
+ client = g_socket_client_new ();
+ g_socket_client_set_tls (client, TRUE);
+ /* The test program uses a self-signed certificate, so we need to
+ * not validate that.
+ */
+ g_socket_client_set_tls_validation_flags (client, G_TLS_VALIDATE_ALL & ~G_TLS_VALIDATE_CA);
+
+ conn = g_socket_client_connect_to_host (client, "localhost", port,
+ NULL, &error);
+ g_assert_no_error (error);
+}
+
+static void
+test_tls_sync (void)
+{
+ int i;
+ char writebuf[BUFSIZE], readbuf[BUFSIZE];
+ gsize n, total;
+ GError *error = NULL;
+
+ for (i = 0; i < BUFSIZE; i++)
+ writebuf[i] = i & 0xFF;
+
+ ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+ istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+
+ total = 0;
+ while (total < BUFSIZE)
+ {
+ n = g_output_stream_write (ostream, writebuf + total,
+ BUFSIZE - total, NULL, &error);
+ g_assert_no_error (error);
+ total += n;
+ }
+
+ total = 0;
+ while (total < BUFSIZE)
+ {
+ n = g_input_stream_read (istream, readbuf + total,
+ BUFSIZE - total, NULL, &error);
+ g_assert_no_error (error);
+ total += n;
+ }
+
+ if (memcmp (writebuf, readbuf, BUFSIZE) != 0)
+ g_error ("Sync read didn't match write");
+}
+
+static void
+test_tls_async (void)
+{
+ g_idle_add (start_writing, conn);
+ loop = g_main_loop_new (NULL, TRUE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+ g_main_context_unref (g_main_context_default ());
+
+ g_object_unref (conn);
+ g_thread_join (server);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ tls_test_setup ();
+
+ g_test_add_func ("/tls/sync", test_tls_sync);
+ g_test_add_func ("/tls/async", test_tls_async);
+
+ return g_test_run();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]