Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -16,19 +16,21 @@ INSTALL_DATA = @INSTALL_DATA@ VPATH = @srcdir@ all: @EXTENSION_TARGET@ +ifeq (@TCLEXT_BUILD@,shared) # The shared object target -tcltls.@SHOBJEXT@: tls.o tlsBIO.o tlsIO.o tlsX509.o Makefile +@EXTENSION_TARGET@: tls.o tlsBIO.o tlsIO.o tlsX509.o Makefile $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o tcltls.@SHOBJEXT@ tls.o tlsBIO.o tlsIO.o tlsX509.o $(LIBS) - +else # The static target -tcltls.@AREXT@: tls.o tlsBIO.o tlsIO.o tlsX509.o Makefile +@EXTENSION_TARGET@: tls.o tlsBIO.o tlsIO.o tlsX509.o Makefile $(AR) rcu tcltls.a.new tls.o tlsBIO.o tlsIO.o tlsX509.o - $(RANLIB) tcltls.a.new + -$(RANLIB) tcltls.a.new mv tcltls.a.new tcltls.a +endif # Dependencies for all our targets tls.o: @srcdir@/tls.c @srcdir@/tlsInt.h @srcdir@/tclOpts.h tls.tcl.h dh_params.h Makefile tlsBIO.o: @srcdir@/tlsBIO.c @srcdir@/tlsInt.h Makefile tlsIO.o: @srcdir@/tlsIO.c @srcdir@/tlsInt.h Makefile Index: aclocal/tcltls_openssl.m4 ================================================================== --- aclocal/tcltls_openssl.m4 +++ aclocal/tcltls_openssl.m4 @@ -1,19 +1,46 @@ dnl $1 = Name of variable dnl $2 = Name of function to check for dnl $3 = Name of protocol dnl $4 = Name of CPP macro to define +dnl $5 = Name of CPP macro to check for instead of a function AC_DEFUN([TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER], [ dnl Determine if particular SSL version is enabled if test "[$]$1" = "true" -o "[$]$1" = "force"; then - AC_CHECK_FUNC($2,, [ + proto_check='true' + ifelse($5,, [ + AC_CHECK_FUNC($2,, [ + proto_check='false' + ]) + ], [ + AC_LANG_PUSH(C) + AC_MSG_CHECKING([for $3 protocol support]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +#if (SSLEAY_VERSION_NUMBER >= 0x0907000L) +# include +#endif + ], [ +int x = $5; + ])], [ + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_RESULT([no]) + + proto_check='false' + ]) + AC_LANG_POP([C]) + ]) + + if test "$proto_check" = 'false'; then if test "[$]$1" = "force"; then AC_MSG_ERROR([Unable to enable $3]) fi $1='false' - ]) + fi fi if test "[$]$1" = "false"; then AC_DEFINE($4, [1], [Define this to disable $3 in OpenSSL support]) fi @@ -153,15 +180,17 @@ AC_MSG_RESULT([no]) AC_MSG_ERROR([Unable to compile a basic program using OpenSSL]) ]) AC_LANG_POP([C]) + AC_CHECK_FUNCS([TLS_method]) TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER([tcltls_ssl_ssl2], [SSLv2_method], [sslv2], [NO_SSL2]) TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER([tcltls_ssl_ssl3], [SSLv3_method], [sslv3], [NO_SSL3]) TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER([tcltls_ssl_tls1_0], [TLSv1_method], [tlsv1.0], [NO_TLS1]) TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER([tcltls_ssl_tls1_1], [TLSv1_1_method], [tlsv1.1], [NO_TLS1_1]) TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER([tcltls_ssl_tls1_2], [TLSv1_2_method], [tlsv1.2], [NO_TLS1_2]) + TCLTLS_SSL_OPENSSL_CHECK_PROTO_VER([tcltls_ssl_tls1_3], [], [tlsv1.3], [NO_TLS1_3], [SSL_OP_NO_TLSv1_3]) AC_CACHE_VAL([tcltls_cv_func_tlsext_hostname], [ AC_LANG_PUSH(C) AC_MSG_CHECKING([for SSL_set_tlsext_host_name]) AC_LINK_IFELSE([AC_LANG_PROGRAM([ Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -108,19 +108,30 @@ else tcltls_ssl_tls1_1='false' fi ]) -dnl ## TLSv1.1: Enabled by default +dnl ## TLSv1.2: Enabled by default tcltls_ssl_tls1_2='true' AC_ARG_ENABLE([tlsv1.2], AS_HELP_STRING([--disable-tlsv1.2], [disable TLSv1.2 protocol]), [ if test "$enableval" = "yes"; then tcltls_ssl_tls1_2='force' else tcltls_ssl_tls1_2='false' fi ]) + +dnl ## TLSv1.3: Enabled by default +tcltls_ssl_tls1_3='true' +AC_ARG_ENABLE([tlsv1.3], AS_HELP_STRING([--disable-tlsv1.3], [disable TLSv1.3 protocol]), [ + if test "$enableval" = "yes"; then + tcltls_ssl_tls1_3='force' + else + tcltls_ssl_tls1_3='false' + fi +]) + dnl Enable support for a debugging build tcltls_debug='false' AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [enable debugging parameters]), [ if test "$enableval" = "yes"; then Index: tls.c ================================================================== --- tls.c +++ tls.c @@ -59,11 +59,11 @@ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); static int UnimportObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); -static SSL_CTX *CTX_Init(State *statePtr, int proto, char *key, +static SSL_CTX *CTX_Init(State *statePtr, int isServer, int proto, char *key, char *cert, char *CAdir, char *CAfile, char *ciphers, char *DHparams); static int TlsLibInit(int uninitialize); @@ -70,10 +70,11 @@ #define TLS_PROTO_SSL2 0x01 #define TLS_PROTO_SSL3 0x02 #define TLS_PROTO_TLS1 0x04 #define TLS_PROTO_TLS1_1 0x08 #define TLS_PROTO_TLS1_2 0x10 +#define TLS_PROTO_TLS1_3 0x20 #define ENABLED(flag, mask) (((flag) & (mask)) == (mask)) /* * Static data structures */ @@ -496,14 +497,14 @@ Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; { static CONST84 char *protocols[] = { - "ssl2", "ssl3", "tls1", "tls1.1", "tls1.2", NULL + "ssl2", "ssl3", "tls1", "tls1.1", "tls1.2", "tls1.3", NULL }; enum protocol { - TLS_SSL2, TLS_SSL3, TLS_TLS1, TLS_TLS1_1, TLS_TLS1_2, TLS_NONE + TLS_SSL2, TLS_SSL3, TLS_TLS1, TLS_TLS1_1, TLS_TLS1_2, TLS_TLS1_3, TLS_NONE }; Tcl_Obj *objPtr; SSL_CTX *ctx = NULL; SSL *ssl = NULL; STACK_OF(SSL_CIPHER) *sk; @@ -558,10 +559,19 @@ Tcl_AppendResult(interp, "protocol not supported", NULL); return TCL_ERROR; #else ctx = SSL_CTX_new(TLSv1_2_method()); break; #endif + case TLS_TLS1_3: +#if defined(NO_TLS1_3) + Tcl_AppendResult(interp, "protocol not supported", NULL); + return TCL_ERROR; +#else + ctx = SSL_CTX_new(TLS_method()); break; + SSL_CTX_set_min_proto_version (ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version (ctx, TLS1_3_VERSION); +#endif default: break; } if (ctx == NULL) { Tcl_AppendResult(interp, REASON(), (char *) NULL); @@ -735,11 +745,11 @@ char *model = NULL; #ifndef OPENSSL_NO_TLSEXT char *servername = NULL; /* hostname for Server Name Indication */ #endif int ssl2 = 0, ssl3 = 0; - int tls1 = 1, tls1_1 = 1, tls1_2 = 1; + int tls1 = 1, tls1_1 = 1, tls1_2 = 1, tls1_3 = 1; int proto = 0; int verify = 0, require = 0, request = 1; dprintf("Called"); @@ -755,10 +765,13 @@ #if defined(NO_TLS1_1) tls1_1 = 0; #endif #if defined(NO_TLS1_2) tls1_2 = 0; +#endif +#if defined(NO_TLS1_3) + tls1_3 = 0; #endif if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "channel ?options?"); return TCL_ERROR; @@ -799,12 +812,13 @@ OPTBOOL( "-ssl2", ssl2); OPTBOOL( "-ssl3", ssl3); OPTBOOL( "-tls1", tls1); OPTBOOL( "-tls1.1", tls1_1); OPTBOOL( "-tls1.2", tls1_2); + OPTBOOL( "-tls1.3", tls1_3); - OPTBAD( "option", "-cadir, -cafile, -certfile, -cipher, -command, -dhparams, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1 or -tls1.2"); + OPTBAD( "option", "-cadir, -cafile, -certfile, -cipher, -command, -dhparams, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, or tls1.3"); return TCL_ERROR; } if (request) verify |= SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_PEER; if (request && require) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; @@ -813,10 +827,11 @@ proto |= (ssl2 ? TLS_PROTO_SSL2 : 0); proto |= (ssl3 ? TLS_PROTO_SSL3 : 0); proto |= (tls1 ? TLS_PROTO_TLS1 : 0); proto |= (tls1_1 ? TLS_PROTO_TLS1_1 : 0); proto |= (tls1_2 ? TLS_PROTO_TLS1_2 : 0); + proto |= (tls1_3 ? TLS_PROTO_TLS1_3 : 0); /* reset to NULL if blank string provided */ if (cert && !*cert) cert = NULL; if (key && !*key) key = NULL; if (ciphers && !*ciphers) ciphers = NULL; @@ -870,11 +885,11 @@ Tls_Free((char *) statePtr); return TCL_ERROR; } ctx = ((State *)Tcl_GetChannelInstanceData(chan))->ctx; } else { - if ((ctx = CTX_Init(statePtr, proto, key, cert, CAdir, CAfile, ciphers, + if ((ctx = CTX_Init(statePtr, server, proto, key, cert, CAdir, CAfile, ciphers, DHparams)) == (SSL_CTX*)0) { Tls_Free((char *) statePtr); return TCL_ERROR; } } @@ -1039,12 +1054,13 @@ * *------------------------------------------------------------------- */ static SSL_CTX * -CTX_Init(statePtr, proto, key, cert, CAdir, CAfile, ciphers, DHparams) +CTX_Init(statePtr, isServer, proto, key, cert, CAdir, CAfile, ciphers, DHparams) State *statePtr; + int isServer; int proto; char *key; char *cert; char *CAdir; char *CAfile; @@ -1094,10 +1110,16 @@ if (ENABLED(proto, TLS_PROTO_TLS1_2)) { Tcl_AppendResult(interp, "protocol not supported", NULL); return (SSL_CTX *)0; } #endif +#if defined(NO_TLS1_3) + if (ENABLED(proto, TLS_PROTO_TLS1_3)) { + Tcl_AppendResult(interp, "protocol not supported", NULL); + return (SSL_CTX *)0; + } +#endif switch (proto) { #if !defined(NO_SSL2) case TLS_PROTO_SSL2: method = SSLv2_method (); @@ -1120,13 +1142,27 @@ #endif #if !defined(NO_TLS1_2) case TLS_PROTO_TLS1_2: method = TLSv1_2_method (); break; +#endif +#if !defined(NO_TLS1_3) + case TLS_PROTO_TLS1_3: + /* + * The version range is constrained below, + * after the context is created. Use the + * generic method here. + */ + method = TLS_method (); + break; #endif default: +#ifdef HAVE_TLS_METHOD + method = TLS_method (); +#else method = SSLv23_method (); +#endif #if !defined(NO_SSL2) off |= (ENABLED(proto, TLS_PROTO_SSL2) ? 0 : SSL_OP_NO_SSLv2); #endif #if !defined(NO_SSL3) off |= (ENABLED(proto, TLS_PROTO_SSL3) ? 0 : SSL_OP_NO_SSLv3); @@ -1138,14 +1174,28 @@ off |= (ENABLED(proto, TLS_PROTO_TLS1_1) ? 0 : SSL_OP_NO_TLSv1_1); #endif #if !defined(NO_TLS1_2) off |= (ENABLED(proto, TLS_PROTO_TLS1_2) ? 0 : SSL_OP_NO_TLSv1_2); #endif +#if !defined(NO_TLS1_3) + off |= (ENABLED(proto, TLS_PROTO_TLS1_3) ? 0 : SSL_OP_NO_TLSv1_3); +#endif break; } ctx = SSL_CTX_new (method); + + if (!ctx) { + return(NULL); + } + +#if !defined(NO_TLS1_3) + if (proto == TLS_PROTO_TLS1_3) { + SSL_CTX_set_min_proto_version (ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version (ctx, TLS1_3_VERSION); + } +#endif SSL_CTX_set_app_data( ctx, (VOID*)interp); /* remember the interpreter */ SSL_CTX_set_options( ctx, SSL_OP_ALL); /* all SSL bug workarounds */ SSL_CTX_set_options( ctx, off); /* all SSL bug workarounds */ SSL_CTX_sess_set_cache_size( ctx, 128); Index: tls.htm ================================================================== --- tls.htm +++ tls.htm @@ -219,10 +219,12 @@
Enable use of TLS v1. (default: true)
-tls1.1 bool
Enable use of TLS v1.1 (default: true)
-tls1.2 bool
Enable use of TLS v1.2 (default: true)
+
-tls1.3 bool
+
Enable use of TLS v1.3 (default: true)
tls::unimport channel
Index: tls.tcl ================================================================== --- tls.tcl +++ tls.tcl @@ -47,10 +47,11 @@ {* -ssl2 iopts 1} {* -ssl3 iopts 1} {* -tls1 iopts 1} {* -tls1.1 iopts 1} {* -tls1.2 iopts 1} + {* -tls1.3 iopts 1} } # tls::socket and tls::init options as a humane readable string variable socketOptionsNoServer variable socketOptionsServer Index: tlsBIO.c ================================================================== --- tlsBIO.c +++ tlsBIO.c @@ -262,11 +262,11 @@ dprintf("Got BIO_CTRL_EOF"); ret = Tcl_Eof(chan); break; case BIO_CTRL_PENDING: dprintf("Got BIO_CTRL_PENDING"); - ret = ((chan) ? 1 : 0); + ret = ((chan) ? Tcl_InputBuffered(chan) : 0); dprintf("BIO_CTRL_PENDING(%d)", (int) ret); break; case BIO_CTRL_WPENDING: dprintf("Got BIO_CTRL_WPENDING"); ret = 0; Index: tlsIO.c ================================================================== --- tlsIO.c +++ tlsIO.c @@ -743,18 +743,19 @@ dprintf("A timer was found, deleting it"); Tcl_DeleteTimerHandler(statePtr->timer); statePtr->timer = (Tcl_TimerToken) NULL; } - if ((mask & TCL_READABLE) && Tcl_InputBuffered(statePtr->self) > 0) { - /* - * There is interest in readable events and we actually have - * data waiting, so generate a timer to flush that. - */ - dprintf("Creating a new timer since data appears to be waiting"); - statePtr->timer = Tcl_CreateTimerHandler(TLS_TCL_DELAY, - TlsChannelHandlerTimer, (ClientData) statePtr); + if (mask & TCL_READABLE) { + if (Tcl_InputBuffered(statePtr->self) > 0 || BIO_ctrl_pending(statePtr->bio) > 0) { + /* + * There is interest in readable events and we actually have + * data waiting, so generate a timer to flush that. + */ + dprintf("Creating a new timer since data appears to be waiting"); + statePtr->timer = Tcl_CreateTimerHandler(TLS_TCL_DELAY, TlsChannelHandlerTimer, (ClientData) statePtr); + } } } /* *------------------------------------------------------------------- Index: tlsX509.c ================================================================== --- tlsX509.c +++ tlsX509.c @@ -82,10 +82,12 @@ * X509 certificate. * *------------------------------------------------------* */ +#define CERT_STR_SIZE 16384 + Tcl_Obj* Tls_NewX509Obj( interp, cert) Tcl_Interp *interp; X509 *cert; { @@ -96,11 +98,12 @@ char subject[BUFSIZ]; char issuer[BUFSIZ]; char serial[BUFSIZ]; char notBefore[BUFSIZ]; char notAfter[BUFSIZ]; - char certStr[BUFSIZ]; + char certStr[CERT_STR_SIZE], *certStr_p; + int certStr_len, toRead; #ifndef NO_SSL_SHA int shai; char sha_hash_ascii[SHA_DIGEST_LENGTH * 2 + 1]; unsigned char sha_hash_binary[SHA_DIGEST_LENGTH]; const char *shachars="0123456789ABCDEF"; @@ -134,13 +137,27 @@ n = max(n, 0); serial[n] = 0; (void)BIO_flush(bio); if (PEM_write_bio_X509(bio, cert)) { - n = BIO_read(bio, certStr, min(BIO_pending(bio), BUFSIZ - 1)); - n = max(n, 0); - certStr[n] = 0; + certStr_p = certStr; + certStr_len = 0; + while (1) { + toRead = min(BIO_pending(bio), CERT_STR_SIZE - certStr_len - 1); + toRead = min(toRead, BUFSIZ); + if (toRead == 0) { + break; + } + dprintf("Reading %i bytes from the certificate...", toRead); + n = BIO_read(bio, certStr_p, toRead); + if (n <= 0) { + break; + } + certStr_len += n; + certStr_p += n; + } + *certStr_p = '\0'; (void)BIO_flush(bio); } BIO_free(bio); }