diff options
author | Ruslan N. Marchenko <me@ruff.mobi> | 2020-04-23 23:33:55 +0200 |
---|---|---|
committer | Ruslan N. Marchenko <me@ruff.mobi> | 2020-05-17 22:39:30 +0200 |
commit | 71d67e44ce3072ebeae477f1b493bbb80f6f7958 (patch) | |
tree | caf29b1acfbd794d21e5f4808711a64bd5e3a40c | |
parent | aa31ef0b5192bf674044eec9678c08250405453f (diff) |
Remove direct openssl backend, available via env GIO_USE_TLS
-rw-r--r-- | configure.ac | 78 | ||||
-rw-r--r-- | examples/Makefile.am | 4 | ||||
-rw-r--r-- | wocky/Makefile.am | 21 | ||||
-rw-r--r-- | wocky/wocky-openssl-dh1024.c | 45 | ||||
-rw-r--r-- | wocky/wocky-openssl-dh2048.c | 56 | ||||
-rw-r--r-- | wocky/wocky-openssl-dh4096.c | 77 | ||||
-rw-r--r-- | wocky/wocky-openssl-dh512.c | 40 | ||||
-rw-r--r-- | wocky/wocky-openssl.c | 2127 |
8 files changed, 6 insertions, 2442 deletions
diff --git a/configure.ac b/configure.ac index f1d339a..6a99912 100644 --- a/configure.ac +++ b/configure.ac @@ -128,81 +128,10 @@ AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_44], [Prevent post 2.44 AP AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) -dnl Choose an SSL/TLS backend (default gnutls) -AC_ARG_WITH([tls], - AC_HELP_STRING([--with-tls=BACKEND], - [which TLS backend to use (gnutls, openssl, or auto) @<:@default=auto@:>@]), - [], - [with_tls=auto]) - - -AS_CASE([$with_tls], - [gnutls], [PKG_CHECK_MODULES(TLS, [gnutls >= 2.8.2 ])], - [openssl], [USING_OPENSSL=yes - AC_DEFINE(USING_OPENSSL, 1, [Define if using openssl]) - PKG_CHECK_MODULES(TLS, [openssl >= 0.9.8g])], - [auto], [PKG_CHECK_MODULES(TLS, [gnutls >= 2.8.2 ], - [with_tls=gnutls], - [USING_OPENSSL=yes - AC_DEFINE(USING_OPENSSL, 1, [Define if using openssl]) - PKG_CHECK_MODULES(TLS, [openssl >= 0.9.8g],[with_tls=openssl], - AC_MSG_ERROR([Neither gnutls nor openssl found]))])], - [*], AC_MSG_ERROR([Must have a TLS backend (gnutls or openssl)])) - +PKG_CHECK_MODULES(TLS, [gnutls >= 3.0], [ HAVE_TLS=yes ], [ HAVE_TLS=no ]) AC_SUBST(TLS_CFLAGS) AC_SUBST(TLS_LIBS) -AM_CONDITIONAL(USING_OPENSSL, test x$USING_OPENSSL = xyes) - -AC_ARG_ENABLE([prefer-stream-ciphers], - AC_HELP_STRING([--enable-prefer-stream-ciphers], - [prefer stream ciphers over block ciphers to save bandwidth (at the possible expense of security)]), - [prefer_stream_ciphers=$enableval], [prefer_stream_ciphers=no]) - -if test x$prefer_stream_ciphers = xyes; then - AC_DEFINE(ENABLE_PREFER_STREAM_CIPHERS, [], - [Prefer stream ciphers over block ones to save bandwidth]) - if test $with_tls = gnutls; then - # The *-ALL priority strings require gnutls 2.12.0. - # We do this check here and not earlier to avoid accidentally falling - # back to openssl because of the use of --enable-prefer-stream-ciphers. - PKG_CHECK_MODULES(GNUTLS_FOR_STREAM_CIPHERS, [gnutls >= 2.12.0],[], - AC_MSG_ERROR([gnutls 2.12.0 is needed to use --enable-prefer-stream-ciphers])) - fi -fi - - -# ----------------------------------------------------------- -# Make CA certificates path configurable -# Stolen from GIO's TLS -# ----------------------------------------------------------- -AC_MSG_CHECKING([location of system Certificate Authority list]) -AC_ARG_WITH(ca-certificates, - [AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@], - [path to system Certificate Authority list])]) -if test "$with_ca_certificates" = "no"; then - AC_MSG_RESULT([disabled]) -else - if test -z "$with_ca_certificates"; 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_certificates="$f" - fi - done - if test -z "$with_ca_certificates"; then - AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable]) - fi - fi - - AC_MSG_RESULT($with_ca_certificates) - AC_DEFINE_UNQUOTED([GTLS_SYSTEM_CA_CERTIFICATES], ["$with_ca_certificates"], [path to system Certificate Authority list]) -fi - -if test -n "$with_ca_certificates"; then - if ! test -f "$with_ca_certificates"; then - AC_MSG_WARN([Specified certificate authority file '$with_ca_certificates' does not exist]) - fi -fi +AM_CONDITIONAL(HAVE_TLS, test x$HAVE_TLS = xyes) GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` @@ -309,9 +238,6 @@ Configure summary: Debug................: ${enable_debug} Features: - TLS Backend..........: ${with_tls} - Prefer stream ciphers: ${prefer_stream_ciphers} - System CA certs......: ${with_ca_certificates} SASL2 Tests..........: ${HAVE_LIBSASL2} gtk-doc documentation: ${enable_gtk_doc} libiphb integration..: ${have_iphb} diff --git a/examples/Makefile.am b/examples/Makefile.am index cddb8a0..b5b0367 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,9 +1,8 @@ EXAMPLES = -if ! USING_OPENSSL +if HAVE_TLS EXAMPLES += wocky-dump-certificates endif - EXAMPLES += wocky-send-message EXAMPLES += wocky-receive-messages EXAMPLES += wocky-register @@ -13,6 +12,7 @@ INCLUDES := -I$(top_builddir)/wocky wocky_dump_certificates_SOURCES = dump-certificates.c wocky_dump_certificates_CFLAGS = $(TLS_CFLAGS) $(AM_CFLAGS) + wocky_dump_certificates_LDADD = $(TLS_LIBS) $(LDADD) wocky_send_message_SOURCES = send-message.c diff --git a/wocky/Makefile.am b/wocky/Makefile.am index 9871311..7dcca6b 100644 --- a/wocky/Makefile.am +++ b/wocky/Makefile.am @@ -37,15 +37,6 @@ built_sources = \ BUILT_SOURCES = $(built_headers) $(built_sources) -OPENSSL_SRC = \ - wocky-openssl.c \ - wocky-openssl-dh512.c \ - wocky-openssl-dh1024.c \ - wocky-openssl-dh2048.c \ - wocky-openssl-dh4096.c - -GNUTLS_SRC = wocky-tls.c - handwritten_headers = \ wocky.h \ wocky-auth-handler.h \ @@ -168,6 +159,7 @@ handwritten_sources = \ wocky-session.c \ wocky-stanza.c \ wocky-utils.c \ + wocky-tls.c \ wocky-tls-common.c \ wocky-tls-handler.c \ wocky-tls-connector.c \ @@ -177,14 +169,6 @@ handwritten_sources = \ wocky-xmpp-reader.c \ wocky-xmpp-writer.c -if USING_OPENSSL - handwritten_sources += $(OPENSSL_SRC) - EXTRA_DIST += $(GNUTLS_SRC) -else - handwritten_sources += $(GNUTLS_SRC) - EXTRA_DIST += $(OPENSSL_SRC) -endif - libwocky_la_SOURCES = $(handwritten_sources) $(built_sources) \ $(handwritten_headers) $(built_headers) @@ -234,7 +218,7 @@ wocky-signals-marshal.list: $(handwritten_sources) Makefile.am AM_CFLAGS = $(ERROR_CFLAGS) $(GCOV_CFLAGS) \ - @GLIB_CFLAGS@ @LIBXML2_CFLAGS@ @SQLITE_CFLAGS@ @TLS_CFLAGS@ \ + @GLIB_CFLAGS@ @LIBXML2_CFLAGS@ @SQLITE_CFLAGS@ \ @LIBIPHB_CFLAGS@ \ @SOUP_CFLAGS@ \ -DG_LOG_DOMAIN=\"wocky\" \ @@ -246,7 +230,6 @@ libwocky_la_LIBADD = \ @GLIB_LIBS@ \ @LIBXML2_LIBS@ \ @SQLITE_LIBS@ \ - @TLS_LIBS@ \ @LIBIPHB_LIBS@ \ @SOUP_LIBS@ \ $(NULL) diff --git a/wocky/wocky-openssl-dh1024.c b/wocky/wocky-openssl-dh1024.c deleted file mode 100644 index 15b2793..0000000 --- a/wocky/wocky-openssl-dh1024.c +++ /dev/null @@ -1,45 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HEADER_DH_H -#include <openssl/dh.h> -#endif -DH *get_dh1024(void); -DH *get_dh1024(void) - { - static unsigned char dh1024_p[]={ - 0xF4,0x88,0xFD,0x58,0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4, - 0x91,0x07,0x36,0x6B,0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C, - 0x88,0xB3,0x1C,0x7C,0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0, - 0x43,0xF0,0xA5,0x5B,0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D, - 0x38,0xD3,0x34,0xFD,0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C, - 0xDE,0x33,0x21,0x2C,0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40, - 0x18,0x11,0x8D,0x7C,0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03, - 0x19,0xC8,0x07,0x29,0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB, - 0xD0,0x0A,0x50,0x9B,0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D, - 0x41,0x9F,0x9C,0x7C,0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB, - 0xA2,0x5E,0xC3,0x55,0xE9,0x2F,0x78,0xC7, - }; - static unsigned char dh1024_g[]={ - 0x02, - }; - DH *dh; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - int r = 0; -#endif - - if ((dh=DH_new()) == NULL) return(NULL); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - r = DH_set0_pqg(dh, BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL), - NULL, BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL)); - if (!r) - { DH_free(dh); return(NULL); } -#else - dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL); - dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } -#endif - return(dh); - } diff --git a/wocky/wocky-openssl-dh2048.c b/wocky/wocky-openssl-dh2048.c deleted file mode 100644 index f51f5b8..0000000 --- a/wocky/wocky-openssl-dh2048.c +++ /dev/null @@ -1,56 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HEADER_DH_H -#include <openssl/dh.h> -#endif -DH *get_dh2048(void); -DH *get_dh2048(void) - { - static unsigned char dh2048_p[]={ - 0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6, - 0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1, - 0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9, - 0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD, - 0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17, - 0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F, - 0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD, - 0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30, - 0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E, - 0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4, - 0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58, - 0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B, - 0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C, - 0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B, - 0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD, - 0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C, - 0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, - 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, - 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, - 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, - 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, - 0xE9,0x32,0x0B,0x3B, - }; - static unsigned char dh2048_g[]={ - 0x02, - }; - DH *dh; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - int r = 0; -#endif - - if ((dh=DH_new()) == NULL) return(NULL); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - r = DH_set0_pqg(dh, BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL), - NULL, BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL)); - if (!r) - { DH_free(dh); return(NULL); } -#else - dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); - dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } -#endif - return(dh); - } diff --git a/wocky/wocky-openssl-dh4096.c b/wocky/wocky-openssl-dh4096.c deleted file mode 100644 index c72f903..0000000 --- a/wocky/wocky-openssl-dh4096.c +++ /dev/null @@ -1,77 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HEADER_DH_H -#include <openssl/dh.h> -#endif -DH *get_dh4096(void); -DH *get_dh4096(void) - { - static unsigned char dh4096_p[]={ - 0xFA,0x14,0x72,0x52,0xC1,0x4D,0xE1,0x5A,0x49,0xD4,0xEF,0x09, - 0x2D,0xC0,0xA8,0xFD,0x55,0xAB,0xD7,0xD9,0x37,0x04,0x28,0x09, - 0xE2,0xE9,0x3E,0x77,0xE2,0xA1,0x7A,0x18,0xDD,0x46,0xA3,0x43, - 0x37,0x23,0x90,0x97,0xF3,0x0E,0xC9,0x03,0x50,0x7D,0x65,0xCF, - 0x78,0x62,0xA6,0x3A,0x62,0x22,0x83,0xA1,0x2F,0xFE,0x79,0xBA, - 0x35,0xFF,0x59,0xD8,0x1D,0x61,0xDD,0x1E,0x21,0x13,0x17,0xFE, - 0xCD,0x38,0x87,0x9E,0xF5,0x4F,0x79,0x10,0x61,0x8D,0xD4,0x22, - 0xF3,0x5A,0xED,0x5D,0xEA,0x21,0xE9,0x33,0x6B,0x48,0x12,0x0A, - 0x20,0x77,0xD4,0x25,0x60,0x61,0xDE,0xF6,0xB4,0x4F,0x1C,0x63, - 0x40,0x8B,0x3A,0x21,0x93,0x8B,0x79,0x53,0x51,0x2C,0xCA,0xB3, - 0x7B,0x29,0x56,0xA8,0xC7,0xF8,0xF4,0x7B,0x08,0x5E,0xA6,0xDC, - 0xA2,0x45,0x12,0x56,0xDD,0x41,0x92,0xF2,0xDD,0x5B,0x8F,0x23, - 0xF0,0xF3,0xEF,0xE4,0x3B,0x0A,0x44,0xDD,0xED,0x96,0x84,0xF1, - 0xA8,0x32,0x46,0xA3,0xDB,0x4A,0xBE,0x3D,0x45,0xBA,0x4E,0xF8, - 0x03,0xE5,0xDD,0x6B,0x59,0x0D,0x84,0x1E,0xCA,0x16,0x5A,0x8C, - 0xC8,0xDF,0x7C,0x54,0x44,0xC4,0x27,0xA7,0x3B,0x2A,0x97,0xCE, - 0xA3,0x7D,0x26,0x9C,0xAD,0xF4,0xC2,0xAC,0x37,0x4B,0xC3,0xAD, - 0x68,0x84,0x7F,0x99,0xA6,0x17,0xEF,0x6B,0x46,0x3A,0x7A,0x36, - 0x7A,0x11,0x43,0x92,0xAD,0xE9,0x9C,0xFB,0x44,0x6C,0x3D,0x82, - 0x49,0xCC,0x5C,0x6A,0x52,0x42,0xF8,0x42,0xFB,0x44,0xF9,0x39, - 0x73,0xFB,0x60,0x79,0x3B,0xC2,0x9E,0x0B,0xDC,0xD4,0xA6,0x67, - 0xF7,0x66,0x3F,0xFC,0x42,0x3B,0x1B,0xDB,0x4F,0x66,0xDC,0xA5, - 0x8F,0x66,0xF9,0xEA,0xC1,0xED,0x31,0xFB,0x48,0xA1,0x82,0x7D, - 0xF8,0xE0,0xCC,0xB1,0xC7,0x03,0xE4,0xF8,0xB3,0xFE,0xB7,0xA3, - 0x13,0x73,0xA6,0x7B,0xC1,0x0E,0x39,0xC7,0x94,0x48,0x26,0x00, - 0x85,0x79,0xFC,0x6F,0x7A,0xAF,0xC5,0x52,0x35,0x75,0xD7,0x75, - 0xA4,0x40,0xFA,0x14,0x74,0x61,0x16,0xF2,0xEB,0x67,0x11,0x6F, - 0x04,0x43,0x3D,0x11,0x14,0x4C,0xA7,0x94,0x2A,0x39,0xA1,0xC9, - 0x90,0xCF,0x83,0xC6,0xFF,0x02,0x8F,0xA3,0x2A,0xAC,0x26,0xDF, - 0x0B,0x8B,0xBE,0x64,0x4A,0xF1,0xA1,0xDC,0xEE,0xBA,0xC8,0x03, - 0x82,0xF6,0x62,0x2C,0x5D,0xB6,0xBB,0x13,0x19,0x6E,0x86,0xC5, - 0x5B,0x2B,0x5E,0x3A,0xF3,0xB3,0x28,0x6B,0x70,0x71,0x3A,0x8E, - 0xFF,0x5C,0x15,0xE6,0x02,0xA4,0xCE,0xED,0x59,0x56,0xCC,0x15, - 0x51,0x07,0x79,0x1A,0x0F,0x25,0x26,0x27,0x30,0xA9,0x15,0xB2, - 0xC8,0xD4,0x5C,0xCC,0x30,0xE8,0x1B,0xD8,0xD5,0x0F,0x19,0xA8, - 0x80,0xA4,0xC7,0x01,0xAA,0x8B,0xBA,0x53,0xBB,0x47,0xC2,0x1F, - 0x6B,0x54,0xB0,0x17,0x60,0xED,0x79,0x21,0x95,0xB6,0x05,0x84, - 0x37,0xC8,0x03,0xA4,0xDD,0xD1,0x06,0x69,0x8F,0x4C,0x39,0xE0, - 0xC8,0x5D,0x83,0x1D,0xBE,0x6A,0x9A,0x99,0xF3,0x9F,0x0B,0x45, - 0x29,0xD4,0xCB,0x29,0x66,0xEE,0x1E,0x7E,0x3D,0xD7,0x13,0x4E, - 0xDB,0x90,0x90,0x58,0xCB,0x5E,0x9B,0xCD,0x2E,0x2B,0x0F,0xA9, - 0x4E,0x78,0xAC,0x05,0x11,0x7F,0xE3,0x9E,0x27,0xD4,0x99,0xE1, - 0xB9,0xBD,0x78,0xE1,0x84,0x41,0xA0,0xDF, - }; - static unsigned char dh4096_g[]={ - 0x02, - }; - DH *dh; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - int r = 0; -#endif - - if ((dh=DH_new()) == NULL) return(NULL); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - r = DH_set0_pqg(dh, BN_bin2bn(dh4096_p,sizeof(dh4096_p),NULL), - NULL, BN_bin2bn(dh4096_g,sizeof(dh4096_g),NULL)); - if (!r) - { DH_free(dh); return(NULL); } -#else - dh->p=BN_bin2bn(dh4096_p,sizeof(dh4096_p),NULL); - dh->g=BN_bin2bn(dh4096_g,sizeof(dh4096_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } -#endif - return(dh); - } diff --git a/wocky/wocky-openssl-dh512.c b/wocky/wocky-openssl-dh512.c deleted file mode 100644 index 885fdc4..0000000 --- a/wocky/wocky-openssl-dh512.c +++ /dev/null @@ -1,40 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HEADER_DH_H -#include <openssl/dh.h> -#endif -DH *get_dh512(void); -DH *get_dh512(void) - { - static unsigned char dh512_p[]={ - 0xF5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, - 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, - 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, - 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, - 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, - 0xE9,0x2A,0x05,0x5F, - }; - static unsigned char dh512_g[]={ - 0x02, - }; - DH *dh; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - int r = 0; -#endif - - if ((dh=DH_new()) == NULL) return(NULL); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - r = DH_set0_pqg(dh, BN_bin2bn(dh512_p,sizeof(dh512_p),NULL), - NULL, BN_bin2bn(dh512_g,sizeof(dh512_g),NULL)); - if (!r) - { DH_free(dh); return(NULL); } -#else - dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL); - dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } -#endif - return(dh); - } diff --git a/wocky/wocky-openssl.c b/wocky/wocky-openssl.c deleted file mode 100644 index 18f9981..0000000 --- a/wocky/wocky-openssl.c +++ /dev/null @@ -1,2127 +0,0 @@ -/* - * Copyright © 2008 Christian Kellner, Samuel Cormier-Iijima - * Copyright © 2008-2009 Codethink Limited - * Copyright © 2009-2010 Collabora Limited - * - * 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. - * - * Authors: Vivek Dasmohapatra <vivek@collabora.co.uk> - * Ryan Lortie <desrt@desrt.ca> - * Christian Kellner <gicmo@gnome.org> - * Samuel Cormier-Iijima <sciyoshi@gmail.com> - * - * Based on wocky-tls.c, which was in turn based on an unmerged gnio feature. - * See wocky-tls.c for details. - * - * This file follows the original coding style from upstream, not collabora - * house style. See wocky-tls.c for details. - */ - -/** - * SECTION: wocky-tls - * @title: Wocky OpenSSL TLS - * @short_description: Establish TLS sessions - * - * The WOCKY_TLS_DEBUG_LEVEL environment variable can be used to print debug - * output from OpenSSL. To enable it, set it to a value from 1 to 9. - * Higher values will print more information. - * - * Increasing the value past certain thresholds will also trigger increased - * debugging output from within wocky-openssl.c as well. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "wocky-tls.h" - -/* Apparently an implicit requirement of OpenSSL's headers... */ -#ifdef G_OS_WIN32 -#include <windows.h> -#endif - -#include <openssl/ssl.h> - -#include <sys/stat.h> -#include <sys/types.h> - -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif - -#include <openssl/err.h> -#include <openssl/engine.h> -#include <openssl/x509v3.h> - -#define WOCKY_DEBUG_FLAG WOCKY_DEBUG_TLS -#define DEBUG_HANDSHAKE_LEVEL 5 -#define DEBUG_ASYNC_DETAIL_LEVEL 6 - -#include "wocky-debug-internal.h" -#include "wocky-utils.h" - -#include <openssl/ssl.h> -#include <openssl/x509_vfy.h> - -#include <ctype.h> -#include <string.h> -#include <errno.h> -#include <sys/types.h> - -/* SSL_CTX_set_cipher_list() allows to restrict/alter the list of supported - * ciphers; see ciphers(1) for documentation on the format. - * Usually the normal ciphers are ok, but on mobile phones we prefer RC4 as - * it decreases the size of packets. The bandwidth difference is tiny, but - * the difference in power consumption between small and very small packets - * can be significant on 3G. */ -#ifdef ENABLE_PREFER_STREAM_CIPHERS - -#define CIPHER_LIST \ - "RC4-SHA:" \ - "RC4-MD5:" \ - "ECDHE-RSA-RC4-SHA:" \ - "ECDHE-ECDSA-RC4-SHA:" \ - "ECDH-RSA-RC4-SHA:" \ - "ECDH-ECDSA-RC4-SHA:" \ - "PSK-RC4-SHA:" \ - "ALL" /* fall-back to all the other algorithms */ - -#endif - -enum -{ - PROP_S_NONE, - PROP_S_STREAM, - PROP_S_SERVER, - PROP_S_DHBITS, - PROP_S_KEYFILE, - PROP_S_CERTFILE, -}; - -enum -{ - PROP_C_NONE, - PROP_C_SESSION, -}; - -enum -{ - PROP_O_NONE, - PROP_O_SESSION -}; - -enum -{ - PROP_I_NONE, - PROP_I_SESSION -}; - -typedef enum -{ - WOCKY_TLS_OP_HANDSHAKE, - WOCKY_TLS_OP_READ, - WOCKY_TLS_OP_WRITE -} WockyTLSOperation; - -/* from openssl docs: not clear if this is exported as a constant by openssl */ -#define MAX_SSLV3_BLOCK_SIZE 0x4000 - -typedef struct -{ - gboolean active; - - gint io_priority; - GCancellable *cancellable; - GObject *source_object; - GAsyncReadyCallback callback; - gpointer user_data; - gpointer source_tag; - GError *error; - gboolean sync_complete; - gchar *buffer; - gsize count; - gchar rbuf[MAX_SSLV3_BLOCK_SIZE]; -} WockyTLSJob; - -typedef struct -{ - WockyTLSJob job; - gulong state; - gboolean done; -} WockyTLSHandshake; - -typedef GIOStreamClass WockyTLSConnectionClass; -typedef GObjectClass WockyTLSSessionClass; -typedef GInputStreamClass WockyTLSInputStreamClass; -typedef GOutputStreamClass WockyTLSOutputStreamClass; - -struct _WockyTLSSession -{ - GObject parent; - - GIOStream *stream; - GCancellable *cancellable; - GError *error; - gboolean async; - - /* tls server support */ - gboolean server; - guint dh_bits; - gchar *key_file; - gchar *cert_file; - - /* frontend jobs */ - struct - { - WockyTLSHandshake handshake; - WockyTLSJob read; - WockyTLSJob write; - } job; - - /* openssl structures */ - BIO *rbio; - BIO *wbio; - SSL_METHOD *method; - SSL_CTX *ctx; - SSL *ssl; -}; - -typedef struct -{ - GInputStream parent; - WockyTLSSession *session; -} WockyTLSInputStream; - -typedef struct -{ - GOutputStream parent; - WockyTLSSession *session; -} WockyTLSOutputStream; - -struct _WockyTLSConnection -{ - GIOStream parent; - - WockyTLSSession *session; - WockyTLSInputStream *input; - WockyTLSOutputStream *output; -}; - -DH * get_dh4096 (void); -DH * get_dh2048 (void); -DH * get_dh1024 (void); -DH * get_dh512 (void); - -static guint tls_debug_level = 0; - -static GType wocky_tls_input_stream_get_type (void); -static GType wocky_tls_output_stream_get_type (void); -G_DEFINE_TYPE (WockyTLSConnection, wocky_tls_connection, G_TYPE_IO_STREAM); -G_DEFINE_TYPE (WockyTLSSession, wocky_tls_session, G_TYPE_OBJECT); -G_DEFINE_TYPE (WockyTLSInputStream, wocky_tls_input_stream, G_TYPE_INPUT_STREAM); -G_DEFINE_TYPE (WockyTLSOutputStream, wocky_tls_output_stream, G_TYPE_OUTPUT_STREAM); -#define WOCKY_TYPE_TLS_INPUT_STREAM (wocky_tls_input_stream_get_type ()) -#define WOCKY_TYPE_TLS_OUTPUT_STREAM (wocky_tls_output_stream_get_type ()) -#define WOCKY_TLS_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ - WOCKY_TYPE_TLS_INPUT_STREAM, \ - WockyTLSInputStream)) -#define WOCKY_TLS_OUTPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ - WOCKY_TYPE_TLS_OUTPUT_STREAM, \ - WockyTLSOutputStream)) - -/* Ok: This function tries to retrieve the error that caused a problem from * - * bottom of the openssl error stack: The errnum argument is the error code * - * returned by the last openssl operation which MAY NOT have come from the * - * openssl error stack (cf SSL_get_error) and which MAY be SSL_ERROR_NONE: * - * it's not supposed to be SSL_ERROR_NONE if a problem occurred, but this is * - * not actually guaranteed anywhere so we have to check for it here: */ -static const gchar *error_to_string (long error) -{ - static gchar ssl_error[256]; - int e; - int x; - /* SSL_ERROR_NONE from ERR_get_error means we have emptied the stack, * - * in which case we should back up and use the last error we saw: */ - for (e = x = error; x != SSL_ERROR_NONE; x = ERR_get_error ()) - e = x; - - /* we found an error in the stack, or were passed one in errnum: */ - if (e != SSL_ERROR_NONE) - { - ERR_error_string_n ((gulong) e, ssl_error, sizeof (ssl_error)); - return ssl_error; - } - - /* No useful/informative/relevant error found */ - return NULL; -} - -static GSimpleAsyncResult * -wocky_tls_job_make_result (WockyTLSJob *job, - gssize result) -{ - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (job->source_object, - job->callback, - job->user_data, - job->source_tag); - if (job->error != NULL) - { - DEBUG ("setting error from job '%s'", job->error->message); - g_simple_async_result_set_from_error (simple, job->error); - g_error_free (job->error); - job->error = NULL; - } - - if (job->source_object != NULL) - g_object_unref (job->source_object); - - job->source_object = NULL; - - if (job->cancellable != NULL) - g_object_unref (job->cancellable); - - job->cancellable = NULL; - - job->active = FALSE; - - return simple; -} - -static void -wocky_tls_job_result_gssize (WockyTLSJob *job, - gssize result) -{ - GSimpleAsyncResult *simple; - - if ((simple = wocky_tls_job_make_result (job, result))) - { - if (result >= 0) - g_simple_async_result_set_op_res_gssize (simple, result); - - g_simple_async_result_complete (simple); - g_object_unref (simple); - } -} - -/* only used for handshake results: read + write use result_gssize */ -static void -wocky_tls_job_result_boolean (WockyTLSJob *job, - gint result) -{ - GSimpleAsyncResult *simple; - - if ((simple = wocky_tls_job_make_result (job, result))) - { - g_simple_async_result_complete (simple); - g_object_unref (simple); - } -} - -/* ************************************************************************* */ -static void -wocky_tls_session_try_operation (WockyTLSSession *session, - WockyTLSOperation operation); - -static void -wocky_tls_session_write_ready (GObject *object, - GAsyncResult *result, - gpointer user_data); -static void -wocky_tls_session_read_ready (GObject *object, - GAsyncResult *result, - gpointer user_data); - -/* writes to the internal BIO should always succeed, so we should never - * receive SSL_ERROR_WANT_WRITE: reads, on the other hand, obviously - * depend on how much data we have buffered, so SSL_ERROR_WANT_READ can - * clearly happen */ -static void -handshake_write (WockyTLSSession *session) -{ - gchar *wbuf; - WockyTLSJob *handshake = &(session->job.handshake.job); - GCancellable *cancel = handshake->cancellable; - gint prio = handshake->io_priority; - GOutputStream *output = g_io_stream_get_output_stream (session->stream); - long wsize = BIO_get_mem_data (session->wbio, &wbuf); - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - g_output_stream_write_async (output, wbuf, wsize, prio, cancel, - wocky_tls_session_write_ready, session); -} - -static void -handshake_read (WockyTLSSession *session) -{ - GInputStream *input = g_io_stream_get_input_stream (session->stream); - WockyTLSJob *handshake = (WockyTLSJob *) &session->job.handshake.job; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - g_input_stream_read_async (input, - &(handshake->rbuf), - MAX_SSLV3_BLOCK_SIZE, - handshake->io_priority, - handshake->cancellable, - wocky_tls_session_read_ready, - session); -} - -static int -ssl_handshake (WockyTLSSession *session) -{ - gint result = 1; - gulong errnum = SSL_ERROR_NONE; - gboolean want_read = FALSE; - gboolean want_write = FALSE; - const gchar *errstr = NULL; - gboolean done = session->job.handshake.done; - gboolean fatal = FALSE; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - if (!done) - { - const gchar *method; - - if (session->server) - { - method = "SSL_accept"; - result = SSL_accept (session->ssl); - } - else - { - method = "SSL_connect"; - result = SSL_connect (session->ssl); - } - errnum = SSL_get_error (session->ssl, result); - done = (result == 1); - errstr = error_to_string (errnum); - fatal = (errnum != SSL_ERROR_WANT_READ && - errnum != SSL_ERROR_WANT_WRITE && - errnum != SSL_ERROR_NONE); - DEBUG ("%s - result: %d; error: %ld", method, result, errnum); - DEBUG ("%s : %s", method, errstr); - } - - /* buffered write data means we need to write */ - want_write = BIO_pending (session->wbio) > 0; - - /* check to see if there's data waiting to go out: * - * since writes to a BIO should always succeed, it is possible to * - * have buffered write data after a successful return, but not * - * possible to be waiting on a read, since SSL_connect should not * - * return success if waiting for data to come in */ - if (done) - { - session->job.handshake.done = TRUE; - - if (!want_write) - { - DEBUG ("Handshake completed"); - errnum = session->job.handshake.state = SSL_ERROR_NONE; - } - else - { - DEBUG ("Handshake completed (IO incomplete)"); - g_assert (errnum != SSL_ERROR_WANT_READ); - errnum = SSL_ERROR_WANT_WRITE; - } - } - else - { - DEBUG ("Handshake state: %ld", errnum); - session->job.handshake.state = errnum; - want_read = (errnum == SSL_ERROR_WANT_READ); - } - /* sif we want both a write (buffered data in the BIO) AND a read * - * (SSL_ERROR_WANT_READ) then this will happen when the handshake_write * - * invokes wocky_tls_session_write_ready which will in turn call * - * wocky_tls_session_try_operation which will re-enter handshake * - * and then proceed to fall back through to this block of code */ - - if (!fatal) - { - DEBUG ("want write: %d; want read: %d;", want_write, want_read); - if (want_write) - handshake_write (session); - else if (want_read) - handshake_read (session); - else - wocky_tls_session_try_operation (session, WOCKY_TLS_OP_HANDSHAKE); - } - else - { - DEBUG ("Handshake failed: [%d:%ld] %s", result, errnum, errstr); - if (session->job.handshake.job.error != NULL) - { - g_error_free (session->job.handshake.job.error); - session->job.handshake.job.error = NULL; - } - g_set_error (&(session->job.handshake.job.error), WOCKY_TLS_ERROR, result, - "Handshake failed: %s", errstr); - wocky_tls_session_try_operation (session, WOCKY_TLS_OP_HANDSHAKE); - } - - return errnum; -} - -static void -ssl_fill (WockyTLSSession *session) -{ - GInputStream *input = g_io_stream_get_input_stream (session->stream); - gchar *rbuf = session->job.read.rbuf; - gint prio = session->job.read.io_priority; - GCancellable *cancel = session->job.read.cancellable; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - g_input_stream_read_async (input, rbuf, MAX_SSLV3_BLOCK_SIZE, prio, cancel, - wocky_tls_session_read_ready, session); -} - -static void -ssl_flush (WockyTLSSession *session) -{ - long wsize; - gchar *wbuf; - gint prio = session->job.read.io_priority; - GOutputStream *output = g_io_stream_get_output_stream (session->stream); - GCancellable *cancel = session->job.read.cancellable; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - wsize = BIO_get_mem_data (session->wbio, &wbuf); - - if (wsize > 0) - g_output_stream_write_async (output, wbuf, wsize, prio, cancel, - wocky_tls_session_write_ready, session); -} - -/* FALSE indicates we should go round again and try to get more data */ -static gboolean -ssl_read_is_complete (WockyTLSSession *session, gint result) -{ - /* if the job error is set, we should bail out now, we have failed * - * otherwise: * - * a -ve return with an SSL error of WANT_READ implies an incomplete * - * crypto record: we need to go round again and get more data * - * or: * - * a 0 return means the SSL connection was shut down cleanly */ - if ((session->job.read.error == NULL) && (result <= 0)) - { - int err = SSL_get_error (session->ssl, result); - - switch (err) - { - case SSL_ERROR_WANT_READ: - DEBUG ("Incomplete SSL record, read again"); - return FALSE; - case SSL_ERROR_WANT_WRITE: - g_warning ("read caused write: unsupported TLS re-negotiation?"); - /* deliberately falling through to the default case, having logged a - * more specific warning. - */ - default: - g_set_error (&session->job.read.error, WOCKY_TLS_ERROR, err, - "OpenSSL read: protocol error %d", err); - } - } - - return TRUE; -} - -static void -wocky_tls_session_try_operation (WockyTLSSession *session, - WockyTLSOperation operation) -{ - WockyTLSJob *handshake = &(session->job.handshake.job); - - if (handshake->active || operation == WOCKY_TLS_OP_HANDSHAKE) - { - gint result = session->job.handshake.state; - DEBUG ("async job handshake"); - - if (tls_debug_level >= DEBUG_HANDSHAKE_LEVEL) - DEBUG ("async job handshake: %d", result); - - switch (result) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - DEBUG ("Handshake incomplete..."); - ssl_handshake (session); - break; - case SSL_ERROR_NONE: - DEBUG ("Handshake complete (success): %d", result); - wocky_tls_job_result_boolean (handshake, result); - break; - default: - DEBUG ("Handshake complete (failure): %d", result); - if (handshake->error == NULL) - handshake->error = - g_error_new (WOCKY_TLS_ERROR, result, "Handshake Error"); - wocky_tls_job_result_boolean (handshake, result); - } - } - else if (operation == WOCKY_TLS_OP_READ) - { - gssize result = 0; - gulong pending = 0; - gsize wanted = 0; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG ("async job OP_READ"); - - /* cipherbytes in the BIO != clearbytes after SSL_read */ - wanted = session->job.read.count; - pending = (gulong)BIO_pending (session->rbio); - result = SSL_read (session->ssl, session->job.read.buffer, wanted); - DEBUG ("read %" G_GSSIZE_FORMAT " clearbytes (from %ld cipherbytes)", - result, pending); - - if (ssl_read_is_complete (session, result)) - wocky_tls_job_result_gssize (&session->job.read, result); - else - ssl_fill (session); - } - - else - { /* we have no useful way of mapping SSL cipherbytes to raw * - * clearbytes: it should always be a complete write unless * - * there's been a network error, in which case the utility * - * of a byte count is debatable anyway */ - gssize result = session->job.write.count; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG ("async job OP_WRITE"); - - g_assert (operation == WOCKY_TLS_OP_WRITE); - DEBUG ("wrote %" G_GSSIZE_FORMAT " clearbytes", result); - wocky_tls_job_result_gssize (&session->job.write, result); - } -} - -static void -wocky_tls_job_start (WockyTLSJob *job, - gpointer source_object, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data, - gpointer source_tag) -{ - g_assert (job->active == FALSE); - g_assert (job->cancellable == NULL); - - /* this is always a circular reference, so it will keep the - * session alive for as long as the job is running. - */ - job->source_object = g_object_ref (source_object); - - job->io_priority = io_priority; - if (cancellable != NULL) - job->cancellable = g_object_ref (cancellable); - job->callback = callback; - job->user_data = user_data; - job->source_tag = source_tag; - job->error = NULL; - job->active = TRUE; -} - -typedef gint (*ssl_handler) (SSL *ssl); - -WockyTLSConnection * -wocky_tls_session_handshake (WockyTLSSession *session, - GCancellable *cancellable, - GError **error) -{ - gint result = -1; - gboolean go = TRUE; - gboolean done = FALSE; - ssl_handler handler = session->server ? SSL_accept : SSL_connect; - gboolean want_write = FALSE; - gboolean want_read = FALSE; - gint errnum = SSL_ERROR_NONE; - const gchar *errstr = NULL; - - while (go) - { - DEBUG ("sync SSL handshake loop"); - - if (!done) - { - result = handler (session->ssl); - errnum = SSL_get_error (session->ssl, result); - done = (result == 1); - DEBUG ("SSL_%s: %d:%d", - (handler == SSL_accept) ? "accept" : "connect", - result, errnum); - if (errnum != SSL_ERROR_NONE && - errnum != SSL_ERROR_WANT_READ && - errnum != SSL_ERROR_WANT_WRITE) - { - errstr = error_to_string (errnum); - DEBUG ("SSL handshake error: [%d:%d] %s", result, errnum, errstr); - } - } - - want_write = BIO_pending (session->wbio) > 0; - want_read = (errnum == SSL_ERROR_WANT_READ); - - if (want_write) - { - gchar *wbuf; - GOutputStream *out = g_io_stream_get_output_stream (session->stream); - long wsize = BIO_get_mem_data (session->wbio, &wbuf); - gssize sent = 0; - DEBUG ("sending %ld cipherbytes", wsize); - if (wsize > 0) - sent = g_output_stream_write (out, wbuf, wsize, NULL, error); - DEBUG ("sent %" G_GSSIZE_FORMAT " cipherbytes", sent); - (void) BIO_reset (session->wbio); - } - - if (want_read) - { - char rbuf[MAX_SSLV3_BLOCK_SIZE]; - GInputStream *in = g_io_stream_get_input_stream (session->stream); - gssize bytes = - g_input_stream_read (in, &rbuf, sizeof(rbuf), NULL, error); - DEBUG ("read %" G_GSSIZE_FORMAT " cipherbytes", bytes); - BIO_write (session->rbio, &rbuf, bytes); - } - - switch (errnum) - { - case SSL_ERROR_WANT_WRITE: - /* WANT_WRITE is theoretically impossible, but what the hell */ - case SSL_ERROR_WANT_READ: - break; - case SSL_ERROR_NONE: - DEBUG ("handshake complete, all IO done"); - go = FALSE; - break; - default: - DEBUG ("SSL handshake error: [%d:%d] %s", result, errnum, errstr); - *error = - g_error_new (WOCKY_TLS_ERROR, errnum, "Handshake: %s", errstr); - go = FALSE; - } - } - - if (done) - return g_object_new (WOCKY_TYPE_TLS_CONNECTION, "session", session, NULL); - - return NULL; -} - -/* ************************************************************************* */ -/* adding CA certificates & CRL lists for peer certificate verification */ - -void -wocky_tls_session_add_ca (WockyTLSSession *session, - const gchar *path) -{ - gboolean ok = FALSE; - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) - { - DEBUG ("CA file or path '%s' not accessible", path); - return; - } - - if (g_file_test (path, G_FILE_TEST_IS_DIR)) - { - DEBUG ("Loading CA directory"); - ok = SSL_CTX_load_verify_locations (session->ctx, NULL, path); - } - - if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) - { - DEBUG ("Loading CA file"); - ok = SSL_CTX_load_verify_locations (session->ctx, path, NULL); - } - - if (!ok) - { - gulong e, f; - for (f = e = ERR_get_error (); e != 0; e = ERR_get_error ()) - f = e; - DEBUG ("CA '%s' failed: %s", path, ERR_error_string (f, NULL)); - } - else - DEBUG ("CA '%s' loaded", path); -} - -void -wocky_tls_session_add_crl (WockyTLSSession *session, - const gchar *path) -{ - gboolean ok = FALSE; - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) - { - DEBUG ("CRL file or path '%s' not accessible", path); - return; - } - - if (g_file_test (path, G_FILE_TEST_IS_DIR)) - { - X509_STORE *store = SSL_CTX_get_cert_store (session->ctx); - X509_LOOKUP_METHOD *method = X509_LOOKUP_hash_dir (); - X509_LOOKUP *lookup = X509_STORE_add_lookup (store, method); - DEBUG ("Loading CRL directory"); - ok = X509_LOOKUP_add_dir (lookup, path, X509_FILETYPE_PEM) == 1; - } - - if (g_file_test (path, G_FILE_TEST_IS_REGULAR)) - { - X509_STORE *store = SSL_CTX_get_cert_store (session->ctx); - X509_LOOKUP_METHOD *method = X509_LOOKUP_file (); - X509_LOOKUP *lookup = X509_STORE_add_lookup (store, method); - DEBUG ("Loading CRL file"); - ok = X509_LOOKUP_load_file (lookup, path, X509_FILETYPE_PEM) == 1; - } - - if (!ok) - { - gulong e, f; - for (f = e = ERR_get_error (); e != 0; e = ERR_get_error ()) - f = e; - DEBUG ("'%s' failed: %s\n", path, ERR_error_string (f, NULL)); - } - else - DEBUG ("'%s' loaded\n", path); -} - -/* ************************************************************************* */ - -void -wocky_tls_session_handshake_async (WockyTLSSession *session, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - DEBUG (""); - wocky_tls_job_start (&session->job.handshake.job, session, - io_priority, cancellable, callback, user_data, - wocky_tls_session_handshake_async); - ssl_handshake (session); -} - -WockyTLSConnection * -wocky_tls_session_handshake_finish (WockyTLSSession *session, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); - DEBUG (""); - { - GObject *source_object; - - source_object = g_async_result_get_source_object (result); - g_object_unref (source_object); - g_return_val_if_fail (G_OBJECT (session) == source_object, NULL); - } - - g_return_val_if_fail (wocky_tls_session_handshake_async == - g_simple_async_result_get_source_tag (simple), NULL); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - DEBUG ("connection OK"); - return g_object_new (WOCKY_TYPE_TLS_CONNECTION, "session", session, NULL); -} - -static gboolean -compare_wildcarded_hostname (const char *hostname, const char *certname) -{ - DEBUG ("%s ~ %s", hostname, certname); - - if (g_ascii_strcasecmp (hostname, certname) == 0) - return TRUE; - - /* We only allow leading '*.' wildcards. See the final bullet point of XMPP - * Core §13.7.1.2.1 - * <http://xmpp.org/rfcs/rfc6120.html#security-certificates-generation-server>: - * - * DNS domain names in server certificates MAY contain the wildcard - * character '*' as the complete left-most label within the identifier. - */ - if (g_str_has_prefix (certname, "*.")) - { - const gchar *certname_tail = certname + 2; - const gchar *hostname_tail = index (hostname, '.'); - - if (hostname_tail == NULL) - return FALSE; - - hostname_tail++; - DEBUG ("%s ~ %s", hostname_tail, certname_tail); - return g_ascii_strcasecmp (hostname_tail, certname_tail) == 0; - } - - return FALSE; -} - -static gboolean -check_peer_name (const char *target, X509 *cert) -{ - int i; - gboolean rval = FALSE; - X509_NAME *subject = X509_get_subject_name (cert); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - const STACK_OF(X509_EXTENSION)* extensions = X509_get0_extensions(cert); -#else - const STACK_OF(X509_EXTENSION)* extensions = cert->cert_info->extensions; -#endif - static const long nid[] = { NID_commonName, NID_subject_alt_name, NID_undef }; - - /* first, see if the x509 name contains the info we want: */ - for (i = 0; nid[i] != NID_undef; i++) - { - gssize len = X509_NAME_get_text_by_NID (subject, nid[i], NULL, -1); - if (len > 0) - { - char *cname = g_new0 (gchar, len + 1); - X509_NAME_get_text_by_NID (subject, nid[i], cname, len + 1); - DEBUG ("got cname '%s' from x509 name, nid #%u", cname, i); - rval = compare_wildcarded_hostname (target, cname); - g_free (cname); - } - } - - /* ok, if that failed, we need to dive into the guts of the x509 structure * - * and extract the subject_alt_name from the x509 v3 extensions: if that * - * extension is present, and a string, use that. If it is present, and * - * a multi-value stack, trawl it for the "DNS" entry and use that */ - if (!rval && (extensions != NULL)) - for (i = 0; i < sk_X509_EXTENSION_num(extensions) && !rval; i++) - { - X509_EXTENSION *ext = sk_X509_EXTENSION_value (extensions, i); - ASN1_OBJECT *obj = X509_EXTENSION_get_object (ext); - X509V3_EXT_METHOD *convert = NULL; - long ni = OBJ_obj2nid (obj); - const guchar *p; - char *value = NULL; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - const ASN1_OCTET_STRING* ext_value = X509_EXTENSION_get_data(ext); - int len = ASN1_STRING_length(ext_value); -#else - int len = ext->value->length; -#endif - void *ext_str = NULL; - - if (ni != NID_subject_alt_name) - continue; - - /* OpenSSL >= 1.0 returns a const here, but we need to be also * - * compatible with older versions that return a non-const value, * - * hence the cast */ - if ((convert = (X509V3_EXT_METHOD *) X509V3_EXT_get (ext)) == NULL) - continue; - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - p = ASN1_STRING_get0_data(ext_value); -#else - p = ext->value->data; -#endif - ext_str = ((convert->it != NULL) ? - ASN1_item_d2i (NULL, &p, len, ASN1_ITEM_ptr(convert->it)) : - convert->d2i (NULL, &p, len) ); - - if (ext_str == NULL) - continue; - - if (convert->i2s != NULL) - { - value = convert->i2s (convert, ext_str); - DEBUG ("got cname '%s' from subject_alt_name, which is a string", - value); - rval = compare_wildcarded_hostname (target, value); - OPENSSL_free (value); - } - else if (convert->i2v != NULL) - { - int j; - STACK_OF(CONF_VALUE) *nval = convert->i2v(convert, ext_str, NULL); - for (j = 0; j < sk_CONF_VALUE_num (nval); j++) - { - CONF_VALUE *v = sk_CONF_VALUE_value(nval, j); - if (!wocky_strdiff (v->name, "DNS")) - { - DEBUG ("Got cname '%s' from subject_alt_name, which is a " - "multi-value stack with a 'DNS' entry", v->value); - rval = compare_wildcarded_hostname (target, v->value); - } - } - sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); - } - - if (convert->it) - ASN1_item_free (ext_str, ASN1_ITEM_ptr (convert->it)); - else - convert->ext_free (ext_str); - } - - return rval; -} - -static gboolean -check_peer_names (const char *peer_name, - GStrv extra_identities, - X509 *cert) -{ - gboolean tried = FALSE; - - if (peer_name != NULL) - { - if (check_peer_name (peer_name, cert)) - return TRUE; - - tried = TRUE; - } - - if (extra_identities != NULL) - { - gint i; - - for (i = 0; extra_identities[i] != NULL; i++) - { - if (wocky_strdiff (extra_identities[i], peer_name)) - { - if (check_peer_name (extra_identities[i], cert)) - return TRUE; - - tried = TRUE; - } - } - } - - /* If no peer names were passed it means we didn't want to check the - * certificate against anything. - * If some attempts were made then it means the check failed. */ - return !tried; -} - -GPtrArray * -wocky_tls_session_get_peers_certificate (WockyTLSSession *session, - WockyTLSCertType *type) -{ - STACK_OF(X509) *cert_chain = NULL; - guint cls = 0; - GPtrArray *certificates; - guint i; - - certificates = - g_ptr_array_new_with_free_func ((GDestroyNotify) g_array_unref); - - cert_chain = SSL_get_peer_cert_chain (session->ssl); - - if (cert_chain == NULL) - return NULL; - - if (type != NULL) - *type = WOCKY_TLS_CERT_TYPE_X509; - - cls = sk_X509_num (cert_chain); - - for (i = 0; i < cls; i++) - { - GArray *certificate; - X509 *peer; - gint peer_len; - guchar *peer_buffer; - - peer = sk_X509_value (cert_chain, i); - peer_len = i2d_X509 (peer, NULL); - - certificate = g_array_sized_new (TRUE, TRUE, sizeof (guchar), peer_len); - - peer_buffer = g_malloc (peer_len); - i2d_X509 (peer, &peer_buffer); - peer_buffer -= peer_len; - - g_array_append_vals (certificate, peer_buffer, peer_len); - g_ptr_array_add (certificates, certificate); - - g_free (peer_buffer); - } - - return certificates; -} - -static WockyTLSCertStatus -_cert_status (WockyTLSSession *session, - int ssl_code, - WockyTLSVerificationLevel level, - int old_code) -{ - switch (ssl_code) - { - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: - return WOCKY_TLS_CERT_SIGNER_UNKNOWN; - break; - case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: - case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: - case X509_V_ERR_CERT_SIGNATURE_FAILURE: - case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: - case X509_V_ERR_INVALID_PURPOSE: - case X509_V_ERR_CERT_REJECTED: - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - return WOCKY_TLS_CERT_INVALID; - break; - case X509_V_ERR_CERT_REVOKED: - return WOCKY_TLS_CERT_REVOKED; - break; - case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: - case X509_V_ERR_CERT_NOT_YET_VALID: - return WOCKY_TLS_CERT_NOT_ACTIVE; - break; - case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: - case X509_V_ERR_CERT_HAS_EXPIRED: - return WOCKY_TLS_CERT_EXPIRED; - break; - case X509_V_ERR_OUT_OF_MEM: - return WOCKY_TLS_CERT_INTERNAL_ERROR; - break; - case X509_V_ERR_INVALID_CA: - case X509_V_ERR_CERT_UNTRUSTED: - case X509_V_ERR_AKID_SKID_MISMATCH: - case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: - case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: - return WOCKY_TLS_CERT_SIGNER_UNAUTHORISED; - break; - case X509_V_ERR_PATH_LENGTH_EXCEEDED: - return WOCKY_TLS_CERT_MAYBE_DOS; - break; - case X509_V_ERR_UNABLE_TO_GET_CRL: - /* if we are in STRICT mode, being unable to see the CRL is a - * terminal condition: in NORMAL or LENIENT we can live with it. - * Also, if we re-tried and got the same error, we're just going - * to loop indefinitely, so bail out with the original error. - * NOTE: 'unable to fetch' a CRL is not the same as CRL invalidated - * the certificate, or we'd just turn the CRL checks off when in - * NORMAL or LENIENT mode */ - if (level == WOCKY_TLS_VERIFY_STRICT || - old_code == X509_V_ERR_UNABLE_TO_GET_CRL) - { - return WOCKY_TLS_CERT_INSECURE; - } - else - { - WockyTLSCertStatus status = WOCKY_TLS_CERT_OK; - X509_STORE_CTX *xctx = X509_STORE_CTX_new(); - X509_STORE *store = SSL_CTX_get_cert_store(session->ctx); - X509 *cert = SSL_get_peer_certificate (session->ssl); - STACK_OF(X509) *chain = SSL_get_peer_cert_chain (session->ssl); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - X509_VERIFY_PARAM* param = X509_STORE_get0_param(store); - long old_flags = X509_VERIFY_PARAM_get_flags(param); -#else - long old_flags = store->param->flags; -#endif - long new_flags = old_flags; - DEBUG("No CRL available, but not in strict mode - re-verifying"); - - new_flags &= ~(X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - X509_VERIFY_PARAM_set_flags(param, new_flags); -#else - store->param->flags = new_flags; -#endif - X509_STORE_CTX_init (xctx, store, cert, chain); - X509_STORE_CTX_set_flags (xctx, new_flags); - - if( X509_verify_cert (xctx) < 1 ) - { - int new_code = X509_STORE_CTX_get_error (xctx); - status = _cert_status (session, new_code, level, ssl_code); - } - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - X509_VERIFY_PARAM_set_flags(param, old_flags); -#else - store->param->flags = old_flags; -#endif - X509_STORE_CTX_free (xctx); - X509_free (cert); - - return status; - } - break; - default: - return WOCKY_TLS_CERT_UNKNOWN_ERROR; - } -} - -int -wocky_tls_session_verify_peer (WockyTLSSession *session, - const gchar *peername, - GStrv extra_identities, - WockyTLSVerificationLevel level, - WockyTLSCertStatus *status) -{ - int rval = -1; - X509 *cert; - gboolean lenient = (level == WOCKY_TLS_VERIFY_LENIENT); - - DEBUG (""); - g_assert (status != NULL); - *status = WOCKY_TLS_CERT_OK; - - switch (level) - { - case WOCKY_TLS_VERIFY_STRICT: - case WOCKY_TLS_VERIFY_NORMAL: - case WOCKY_TLS_VERIFY_LENIENT: - break; - default: - g_warn_if_reached (); - level = WOCKY_TLS_VERIFY_STRICT; - } - - DEBUG ("setting ssl verify flags level to: %s", - wocky_enum_to_nick (WOCKY_TYPE_TLS_VERIFICATION_LEVEL, level)); - cert = SSL_get_peer_certificate (session->ssl); - rval = SSL_get_verify_result (session->ssl); - DEBUG ("X509 cert: %p; verified: %d", cert, rval); - - /* If no certificate is presented, SSL_get_verify_result() always returns - * X509_V_OK. This is listed as a bug in `man 3 SSL_get_verify_result`. To - * future-proof against that bug being fixed, we don't assume that behaviour. - */ - if (cert == NULL) - { - if (lenient) - { - *status = WOCKY_TLS_CERT_OK; - return X509_V_OK; - } - else if (rval == X509_V_OK) - { - DEBUG ("Anonymous SSL handshake"); - rval = X509_V_ERR_CERT_UNTRUSTED; - } - } - else if (!check_peer_names (peername, extra_identities, cert)) - { - /* Irrespective of whether the certificate is valid, if it's for the - * wrong host that's arguably a more useful error condition to report. - */ - *status = WOCKY_TLS_CERT_NAME_MISMATCH; - return X509_V_ERR_APPLICATION_VERIFICATION; - } - - if (rval != X509_V_OK) - { - DEBUG ("cert verification error: %d", rval); - *status = _cert_status (session, rval, level, X509_V_OK); - - /* some conditions are to be ignored when lenient, others still matter */ - if (lenient) - switch (*status) - { - case WOCKY_TLS_CERT_INTERNAL_ERROR: - case WOCKY_TLS_CERT_REVOKED: - case WOCKY_TLS_CERT_MAYBE_DOS: - DEBUG ("this error matters, even though we're in lenient mode"); - break; - default: - DEBUG ("ignoring errors: we're in lenient mode"); - rval = X509_V_OK; - *status = WOCKY_TLS_CERT_OK; - } - } - - return rval; -} - -static gssize -wocky_tls_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - /* WockyTLSSession *session = WOCKY_TLS_INPUT_STREAM (stream)->session; */ - - DEBUG ("sync read - not implmented"); - g_assert_not_reached (); - - return 0; -} - -static void -wocky_tls_input_stream_read_async (GInputStream *stream, - void *buffer, - gsize count, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WockyTLSSession *session = WOCKY_TLS_INPUT_STREAM (stream)->session; - int ret; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - g_assert (session->job.read.active == FALSE); - - /* It is possible for a complete SSL record to be present in the read BIO * - * already as a result of a previous read, since SSL_read may extract * - * just the first complete record, or some or all of them: * - * as a result, we may not want to issue an actual read request as the * - * data we are expecting may already have been read, causing us to wait * - * until the next block of data arrives over the network (which may not * - * ever happen): short-circuit the actual read if this is the case: */ - ret = SSL_read (session->ssl, buffer, count); - - if (ssl_read_is_complete (session, ret)) - { - GSimpleAsyncResult *r; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG ("already have %d clearbytes buffered", ret); - - r = g_simple_async_result_new (G_OBJECT (stream), - callback, - user_data, - wocky_tls_input_stream_read_async); - - if (session->job.read.error == NULL) - g_simple_async_result_set_op_res_gssize (r, ret); - else - g_simple_async_result_set_from_error (r, session->job.read.error); - - g_simple_async_result_complete_in_idle (r); - g_object_unref (r); - return; - } - - wocky_tls_job_start (&session->job.read, stream, - io_priority, cancellable, callback, user_data, - wocky_tls_input_stream_read_async); - - session->job.read.buffer = buffer; - session->job.read.count = count; - ssl_fill (session); -} - -static gssize -wocky_tls_input_stream_read_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - g_return_val_if_fail (g_simple_async_result_is_valid (result, - G_OBJECT (stream), wocky_tls_input_stream_read_async), -1); - - if (g_simple_async_result_propagate_error (simple, error)) - return -1; - - return g_simple_async_result_get_op_res_gssize (simple); -} - -static gssize -wocky_tls_output_stream_write (GOutputStream *stream, - const void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - /* WockyTLSSession *session = WOCKY_TLS_OUTPUT_STREAM (stream)->session; */ - - DEBUG ("sync write - not implemented"); - - g_assert_not_reached (); - - return 0; -} - -static void -wocky_tls_output_stream_write_async (GOutputStream *stream, - const void *buffer, - gsize count, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - int code; - WockyTLSSession *session = WOCKY_TLS_OUTPUT_STREAM (stream)->session; - - DEBUG ("%" G_GSIZE_FORMAT " clearbytes to send", count); - wocky_tls_job_start (&session->job.write, stream, - io_priority, cancellable, callback, user_data, - wocky_tls_output_stream_write_async); - - session->job.write.count = count; - - code = SSL_write (session->ssl, buffer, count); - if (code < 0) - { - int error = SSL_get_error (session->ssl, code); - switch (error) - { - case SSL_ERROR_WANT_WRITE: - DEBUG ("Incomplete SSL write to BIO (theoretically impossible)"); - ssl_flush (session); - return; - case SSL_ERROR_WANT_READ: - g_warning ("write caused read: unsupported TLS re-negotiation?"); - /* deliberately falling through to the default case, having logged a - * more specific warning. - */ - default: - DEBUG ("SSL write failed, setting error %d", error); - /* if we haven't already generated an error, set one here: */ - if(session->job.write.error == NULL) - session->job.write.error = - g_error_new (WOCKY_TLS_ERROR, error, - "OpenSSL write: protocol error %d", error); - wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); - return; - } - } - ssl_flush (session); -} - -static gssize -wocky_tls_output_stream_write_finish (GOutputStream *stream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - { - GObject *source_object; - - source_object = g_async_result_get_source_object (result); - g_object_unref (source_object); - g_return_val_if_fail (G_OBJECT (stream) == source_object, -1); - } - - g_return_val_if_fail (wocky_tls_output_stream_write_async == - g_simple_async_result_get_source_tag (simple), -1); - - if (g_simple_async_result_propagate_error (simple, error)) - return -1; - - return g_simple_async_result_get_op_res_gssize (simple); -} - -static void -wocky_tls_output_stream_init (WockyTLSOutputStream *stream) -{ -} - -static void -wocky_tls_input_stream_init (WockyTLSInputStream *stream) -{ -} - -static void -wocky_tls_output_stream_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); - - switch (prop_id) - { - case PROP_C_SESSION: - stream->session = g_value_dup_object (value); - break; - - default: - g_assert_not_reached (); - } -} - -static void -wocky_tls_output_stream_constructed (GObject *object) -{ - WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); - - g_assert (stream->session); -} - -static void -wocky_tls_output_stream_finalize (GObject *object) -{ - WockyTLSOutputStream *stream = WOCKY_TLS_OUTPUT_STREAM (object); - - g_object_unref (stream->session); - - G_OBJECT_CLASS (wocky_tls_output_stream_parent_class) - ->finalize (object); -} - -static void -wocky_tls_output_stream_class_init (GOutputStreamClass *class) -{ - GObjectClass *obj_class = G_OBJECT_CLASS (class); - - class->write_fn = wocky_tls_output_stream_write; - class->write_async = wocky_tls_output_stream_write_async; - class->write_finish = wocky_tls_output_stream_write_finish; - obj_class->set_property = wocky_tls_output_stream_set_property; - obj_class->constructed = wocky_tls_output_stream_constructed; - obj_class->finalize = wocky_tls_output_stream_finalize; - - g_object_class_install_property (obj_class, PROP_O_SESSION, - g_param_spec_object ("session", "TLS session", - "the TLS session object for this stream", - WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); -} - -static void -wocky_tls_input_stream_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); - - switch (prop_id) - { - case PROP_C_SESSION: - stream->session = g_value_dup_object (value); - break; - - default: - g_assert_not_reached (); - } -} - -static void -wocky_tls_input_stream_constructed (GObject *object) -{ - WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); - - g_assert (stream->session); -} - -static void -wocky_tls_input_stream_finalize (GObject *object) -{ - WockyTLSInputStream *stream = WOCKY_TLS_INPUT_STREAM (object); - - g_object_unref (stream->session); - - G_OBJECT_CLASS (wocky_tls_input_stream_parent_class) - ->finalize (object); -} - -static void -wocky_tls_input_stream_class_init (GInputStreamClass *class) -{ - GObjectClass *obj_class = G_OBJECT_CLASS (class); - - class->read_fn = wocky_tls_input_stream_read; - class->read_async = wocky_tls_input_stream_read_async; - class->read_finish = wocky_tls_input_stream_read_finish; - obj_class->set_property = wocky_tls_input_stream_set_property; - obj_class->constructed = wocky_tls_input_stream_constructed; - obj_class->finalize = wocky_tls_input_stream_finalize; - - g_object_class_install_property (obj_class, PROP_I_SESSION, - g_param_spec_object ("session", "TLS session", - "the TLS session object for this stream", - WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); -} - -static void -wocky_tls_connection_init (WockyTLSConnection *connection) -{ -} - -static void -wocky_tls_session_read_ready (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); - GInputStream *input = G_INPUT_STREAM (object); - GError **error = &(session->job.read.error); - gssize rsize = 0; - gchar *buf = session->job.handshake.job.active ? - session->job.handshake.job.rbuf : session->job.read.rbuf; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - rsize = g_input_stream_read_finish (input, result, error); - - if (rsize > 0) - { - int x; - int y; - DEBUG ("received %" G_GSSIZE_FORMAT " cipherbytes, filling SSL BIO", - rsize); - BIO_write (session->rbio, buf, rsize); - if (tls_debug_level > DEBUG_ASYNC_DETAIL_LEVEL + 1) - for (x = 0; x < rsize; x += 16) - { - for (y = 0; y < 16 && x + y < rsize; y++) - { - char c = *(buf + x + y); - char d = (g_ascii_isprint (c) && g_ascii_isgraph (c)) ? c : '.'; - fprintf (stderr, "%02x %c ", c & 0xff, d); - } - fprintf (stderr, "\n"); - } - } - /* note that we never issue a read of 0, so this _must_ be EOF (0) * - * or a fatal error (-ve rval) */ - else if (session->job.handshake.job.active) - { - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG("read SSL cipherbytes (handshake) failed: %" G_GSSIZE_FORMAT, - rsize); - session->job.handshake.state = SSL_ERROR_SSL; - } - else - { - DEBUG ("read of SSL cipherbytes failed: %" G_GSSIZE_FORMAT, rsize); - - if ((*error != NULL) && ((*error)->domain == g_io_error_quark ())) - { - /* if there were any errors we could ignore, we'd do it like this: * - * g_error_free (*error); *error = NULL; */ - DEBUG ("failed op: [%d] %s", (*error)->code, (*error)->message); - } - /* in order for non-handshake reads to return an error properly * - * we need to make sure the error in the job is set */ - else if (*error == NULL) - { - *error = - g_error_new (WOCKY_TLS_ERROR, SSL_ERROR_SSL, "unknown error"); - } - } - - wocky_tls_session_try_operation (session, WOCKY_TLS_OP_READ); -} - -static void -wocky_tls_session_write_ready (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - WockyTLSSession *session = WOCKY_TLS_SESSION (user_data); - gint buffered = BIO_pending (session->wbio); - gssize written; - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG (""); - - written = g_output_stream_write_finish (G_OUTPUT_STREAM (object), result, - &(session->job.write.error)); - - if (written == buffered) - { - DEBUG ("%d bytes written, clearing write BIO", buffered); - (void) BIO_reset (session->wbio); - wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); - } - else - { - gchar *buffer; - long bsize = BIO_get_mem_data (session->wbio, &buffer); - long psize = bsize - written; - - /* scrub the data we did manage to write from our buffer */ - if (written > 0) - { - gchar *pending = g_memdup (buffer + written, psize); - - (void) BIO_reset (session->wbio); - (void) BIO_write (session->wbio, pending, psize); - g_free (pending); - } - - if (session->job.write.error != NULL) - { - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - DEBUG ("Incomplete async write [%" G_GSSIZE_FORMAT "/%d bytes]: " - "%s:%u %s", - written, buffered, - g_quark_to_string (session->job.write.error->domain), - session->job.write.error->code, - session->job.write.error->message); - - /* if we have a non-fatal error, erase it try again */ - if (g_error_matches (session->job.write.error, - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) - g_clear_error (&(session->job.write.error)); - } - - /* no error here means retry the operation; otherwise bail out */ - if (session->job.write.error == NULL) - ssl_flush (session); - else - wocky_tls_session_try_operation (session, WOCKY_TLS_OP_WRITE); - } -} - -static void -wocky_tls_session_init (WockyTLSSession *session) -{ - const char *level; - guint lvl = 0; - static gsize initialised; - - if G_UNLIKELY (g_once_init_enter (&initialised)) - { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - DEBUG ("initialising SSL library and error strings"); -#else - gint malloc_init_succeeded; - - DEBUG ("initialising SSL library and error strings"); - - malloc_init_succeeded = CRYPTO_malloc_init (); - g_warn_if_fail (malloc_init_succeeded); -#endif - - SSL_library_init (); - SSL_load_error_strings (); - OpenSSL_add_all_algorithms(); - ENGINE_load_builtin_engines (); - g_once_init_leave (&initialised, 1); - } - - if ((level = getenv ("WOCKY_TLS_DEBUG_LEVEL")) != NULL) - lvl = atoi (level); - - tls_debug_level = lvl; -} - -static void -wocky_tls_session_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - WockyTLSSession *session = WOCKY_TLS_SESSION (object); - - switch (prop_id) - { - case PROP_S_STREAM: - session->stream = g_value_dup_object (value); - break; - case PROP_S_SERVER: - session->server = g_value_get_boolean (value); - break; - case PROP_S_DHBITS: - session->dh_bits = g_value_get_uint (value); - break; - case PROP_S_KEYFILE: - session->key_file = g_value_dup_string (value); - break; - case PROP_S_CERTFILE: - session->cert_file = g_value_dup_string (value); - break; - default: - g_assert_not_reached (); - } -} - -static void -set_dh_parameters (WockyTLSSession *session) -{ - DH *dh; - - switch (session->dh_bits) - { - case 4096: - DEBUG ("get_dh4096"); - dh = get_dh4096 (); - break; - case 2048: - DEBUG ("get_dh2048"); - dh = get_dh2048 (); - break; - case 1024: - DEBUG ("get_dh1024"); - dh = get_dh1024 (); - break; - case 512: - DEBUG ("get_dh512"); - dh = get_dh512 (); - break; - default: - DEBUG ("Bad dh-bits setting: %d, reset to 1024", session->dh_bits); - dh = get_dh1024 (); - } - - SSL_CTX_set_tmp_dh (session->ctx, dh); - DH_free (dh); -} - -static void -set_ecdh_key (WockyTLSSession *session) -{ - EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_sect163r2); - if (ecdh == NULL) - { - DEBUG ("unable to create elliptical crypto key for sect163r2 curve"); - return; - } - SSL_CTX_set_tmp_ecdh (session->ctx,ecdh); - EC_KEY_free (ecdh); -} - -static void -wocky_tls_session_constructed (GObject *object) -{ - WockyTLSSession *session = WOCKY_TLS_SESSION (object); - - if (session->server) - { - DEBUG ("I'm a server; using TLSv1_server_method"); - /* OpenSSL >= 1.0 returns a const here, but we need to be also * - * compatible with older versions that return a non-const value, * - * hence the cast */ - session->method = (SSL_METHOD *) TLSv1_server_method (); - } - else - { - DEBUG ("I'm a client; using TLSv1_client_method"); - session->method = (SSL_METHOD *) TLSv1_client_method (); - } - - session->ctx = SSL_CTX_new (session->method); - - if (!SSL_CTX_set_default_verify_paths (session->ctx)) - g_warning ("SSL_CTX_set_default_verify_paths() failed"); - - /* verification will be done manually after the handshake: */ - SSL_CTX_set_verify (session->ctx, SSL_VERIFY_NONE, NULL); - SSL_CTX_set_options (session->ctx, - SSL_OP_CIPHER_SERVER_PREFERENCE | - /* It is usually safe to use SSL_OP_ALL to enable the bug workaround - * options if compatibility with somewhat broken implementations is - * desired. - */ - SSL_OP_ALL | - /* Set the NO_TICKET option on the context to be kind to the Google Talk - * server, which seems unwilling to handle empty session tickets due to a - * bug in Java. - * - * See http://twistedmatrix.com/trac/ticket/3463 and - * http://loudmouth.lighthouseapp.com/projects/17276/tickets/28. - */ - SSL_OP_NO_TICKET | - /* SSLv2 is excessively quaint. We shouldn't be using it anyway, since - * we're using TLSv1 methods, but... - */ - SSL_OP_NO_SSLv2); - X509_STORE_set_flags (SSL_CTX_get_cert_store (session->ctx), - X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); - -#ifdef CIPHER_LIST - SSL_CTX_set_cipher_list (session->ctx, CIPHER_LIST); -#endif - - if (session->server) - { - set_dh_parameters (session); - set_ecdh_key (session); - } - - if ((session->key_file != NULL) && (session->cert_file != NULL)) - { - long errnum; - DEBUG ("cert: %s", session->cert_file); - DEBUG ("key : %s", session->key_file); - SSL_CTX_use_certificate_file (session->ctx, - session->cert_file, - SSL_FILETYPE_PEM); - SSL_CTX_use_PrivateKey_file (session->ctx, - session->key_file, - SSL_FILETYPE_PEM); - if (!SSL_CTX_check_private_key (session->ctx)) - { - errnum = ERR_get_error (); - DEBUG ("cert/key check: %ld %s", errnum, error_to_string (errnum)); - } - else - DEBUG ("certificate loaded"); - } - - session->ssl = SSL_new (session->ctx); - session->rbio = BIO_new (BIO_s_mem ()); - session->wbio = BIO_new (BIO_s_mem ()); - - if (session->rbio == NULL) - g_error ("Could not allocate memory BIO for SSL reads"); - - if (session->wbio == NULL) - g_error ("Could not allocate memory BIO for SSL writes"); - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - { - int x = 0; - const char *c = SSL_get_cipher_list (session->ssl, x); - for (; c != NULL; c = SSL_get_cipher_list (session->ssl, ++x)) - DEBUG ("%03d: %s", x, c); - } - - if (tls_debug_level >= DEBUG_ASYNC_DETAIL_LEVEL) - { - BIO_set_callback (session->rbio, BIO_debug_callback); - BIO_set_callback (session->wbio, BIO_debug_callback); - } - - BIO_set_mem_eof_return (session->rbio, -1); - SSL_set_bio (session->ssl, session->rbio, session->wbio); - - DEBUG ("done"); -} - -static void -wocky_tls_session_finalize (GObject *object) -{ - WockyTLSSession *session = WOCKY_TLS_SESSION (object); - - /* the BIOs are freed by this call */ - SSL_free (session->ssl); - /* free (session->method); handled by SSL_CTX_free */ - session->method = NULL; - SSL_CTX_free (session->ctx); - session->ctx = NULL; - - g_object_unref (session->stream); - - G_OBJECT_CLASS (wocky_tls_session_parent_class)->finalize (object); -} - -static void -wocky_tls_session_dispose (GObject *object) -{ - WockyTLSSession *session = WOCKY_TLS_SESSION (object); - - g_free (session->key_file); - session->key_file = NULL; - - g_free (session->cert_file); - session->cert_file = NULL; - - G_OBJECT_CLASS (wocky_tls_session_parent_class)->dispose (object); -} - -static void -wocky_tls_session_class_init (GObjectClass *class) -{ - class->set_property = wocky_tls_session_set_property; - class->constructed = wocky_tls_session_constructed; - class->finalize = wocky_tls_session_finalize; - class->dispose = wocky_tls_session_dispose; - - g_object_class_install_property (class, PROP_S_STREAM, - g_param_spec_object ("base-stream", "base stream", - "the stream that TLS communicates over", - G_TYPE_IO_STREAM, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (class, PROP_S_SERVER, - g_param_spec_boolean ("server", "server", - "whether this is a server", - FALSE, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (class, PROP_S_DHBITS, - g_param_spec_uint ("dh-bits", "Diffie-Hellman bits", - "Diffie-Hellmann bits: 512, 1024, 2048, or 4096", - 512, 4096, 1024, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (class, PROP_S_KEYFILE, - g_param_spec_string ("x509-key", "x509 key", - "x509 PEM key file", - NULL, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (class, PROP_S_CERTFILE, - g_param_spec_string ("x509-cert", "x509 certificate", - "x509 PEM certificate file", - NULL, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); -} - -static void -wocky_tls_connection_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); - - switch (prop_id) - { - case PROP_C_SESSION: - connection->session = g_value_dup_object (value); - break; - - default: - g_assert_not_reached (); - } -} - -static gboolean -wocky_tls_connection_close (GIOStream *stream, GCancellable *cancellable, - GError **error) -{ - WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (stream); - - return g_io_stream_close (connection->session->stream, cancellable, error); -} - -static GInputStream * -wocky_tls_connection_get_input_stream (GIOStream *io_stream) -{ - WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (io_stream); - - if (connection->input == NULL) - connection->input = g_object_new (WOCKY_TYPE_TLS_INPUT_STREAM, - "session", connection->session, - NULL); - - return (GInputStream *)connection->input; -} - -static GOutputStream * -wocky_tls_connection_get_output_stream (GIOStream *io_stream) -{ - WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (io_stream); - - if (connection->output == NULL) - connection->output = g_object_new (WOCKY_TYPE_TLS_OUTPUT_STREAM, - "session", connection->session, - NULL); - - return (GOutputStream *)connection->output; -} - -static void -wocky_tls_connection_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - switch (prop_id) - { - default: - g_assert_not_reached (); - } -} - -static void -wocky_tls_connection_constructed (GObject *object) -{ - WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); - - g_assert (connection->session); -} - -static void -wocky_tls_connection_finalize (GObject *object) -{ - WockyTLSConnection *connection = WOCKY_TLS_CONNECTION (object); - - g_object_unref (connection->session); - - if (connection->input != NULL) - g_object_unref (connection->input); - - if (connection->output != NULL) - g_object_unref (connection->output); - - G_OBJECT_CLASS (wocky_tls_connection_parent_class) - ->finalize (object); -} - -static void -wocky_tls_connection_class_init (WockyTLSConnectionClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class); - - gobject_class->get_property = wocky_tls_connection_get_property; - gobject_class->set_property = wocky_tls_connection_set_property; - gobject_class->constructed = wocky_tls_connection_constructed; - gobject_class->finalize = wocky_tls_connection_finalize; - - g_object_class_install_property (gobject_class, PROP_C_SESSION, - g_param_spec_object ("session", "TLS session", - "the TLS session object for this connection", - WOCKY_TYPE_TLS_SESSION, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - stream_class->get_input_stream = wocky_tls_connection_get_input_stream; - stream_class->get_output_stream = wocky_tls_connection_get_output_stream; - stream_class->close_fn = wocky_tls_connection_close; -} - -WockyTLSSession * -wocky_tls_session_new (GIOStream *stream) -{ - return g_object_new (WOCKY_TYPE_TLS_SESSION, - "base-stream", stream, - "server", FALSE, NULL); -} - -/** - * wocky_tls_session_server_new: - * @stream: a GIOStream on which we expect to receive the client TLS handshake - * @dhbits: size of the DH parameters - * @key: the path to the X509 PEM key file - * @cert: the path to the X509 PEM certificate - * - * Create a new TLS server session - * - * Returns: a #WockyTLSSession object - */ -WockyTLSSession * -wocky_tls_session_server_new (GIOStream *stream, guint dhbits, - const gchar* key, const gchar* cert) -{ - if (dhbits == 0) - dhbits = 1024; - return g_object_new (WOCKY_TYPE_TLS_SESSION, "base-stream", stream, - "dh-bits", dhbits, "x509-key", key, "x509-cert", cert, - "server", TRUE, - NULL); -} - -/* this file is "borrowed" from an unmerged gnio feature: */ -/* Local Variables: */ -/* c-file-style: "gnu" */ -/* End: */ |