@@ -2,10 +2,11 @@ * Copyright (C) 1997-1999 Matt Newman * some modifications: * Copyright (C) 2000 Ajuba Solutions * Copyright (C) 2002 ActiveState Corporation * Copyright (C) 2004 Starfish Systems + * Copyright (C) 2023 Brian O'Hagan * * TLS (aka SSL) Channel - can be layered on any bi-directional * Tcl_Channel (Note: Requires Trf Core Patch) * * This was built (almost) from scratch based upon observation of @@ -22,10 +23,15 @@ */ #include "tlsInt.h" #include "tclOpts.h" #include + +/* Min OpenSSL version */ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10101000L +#error "Only OpenSSL v1.1.1 or later is supported" +#endif /* * External functions */ @@ -270,16 +276,16 @@ int err = X509_STORE_CTX_get_error(ctx); dprintf("Verify: %d", ok); if (!ok) { - errStr = (char*)X509_verify_cert_error_string(err); + errStr = (char *)X509_verify_cert_error_string(err); } else { errStr = (char *)0; } - if (statePtr->callback == (Tcl_Obj*)NULL) { + if (statePtr->callback == NULL) { if (statePtr->vflags & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { return ok; } else { return 1; } @@ -408,16 +414,16 @@ /* * No way to handle user-data therefore no way without a global * variable to access the Tcl interpreter. */ static int -PasswordCallback(char *buf, int size, int verify) +PasswordCallback( + TCL_UNUSED(char *) /* buf */, + TCL_UNUSED(int) /* size */, + TCL_UNUSED(int) /* verify */) { return -1; - buf = buf; - size = size; - verify = verify; } #else static int PasswordCallback(char *buf, int size, TCL_UNUSED(int) /* verify */, void *udata) { @@ -514,52 +520,46 @@ &verbose) != TCL_OK) { return TCL_ERROR; } switch ((enum protocol)index) { case TLS_SSL2: -#if defined(NO_SSL2) Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); return TCL_ERROR; -#else - ctx = SSL_CTX_new(SSLv2_method()); break; -#endif case TLS_SSL3: -#if defined(NO_SSL3) +#if defined(NO_SSL3) || defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD) Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); return TCL_ERROR; #else ctx = SSL_CTX_new(SSLv3_method()); break; #endif case TLS_TLS1: -#if defined(NO_TLS1) +#if defined(NO_TLS1) || defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD) Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); return TCL_ERROR; #else ctx = SSL_CTX_new(TLSv1_method()); break; #endif case TLS_TLS1_1: -#if defined(NO_TLS1_1) +#if defined(NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD) Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); return TCL_ERROR; #else ctx = SSL_CTX_new(TLSv1_1_method()); break; #endif - case TLS_TLS1_2: -#if defined(NO_TLS1_2) +#if defined(NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2_METHOD) Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); return TCL_ERROR; #else ctx = SSL_CTX_new(TLSv1_2_method()); break; #endif - case TLS_TLS1_3: -#if defined(NO_TLS1_3) +#if defined(NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3_METHOD) Tcl_AppendResult(interp, "protocol not supported", (char *)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); + 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) { @@ -747,14 +747,11 @@ int proto = 0; int verify = 0, require = 0, request = 1; dprintf("Called"); -#if defined(NO_TLS1) && defined(NO_TLS1_1) && defined(NO_TLS1_2) && defined(NO_SSL3) && !defined(NO_SSL2) - ssl2 = 1; -#endif -#if defined(NO_TLS1) && defined(NO_TLS1_1) && defined(NO_TLS1_2) && defined(NO_SSL2) && !defined(NO_SSL3) +#if defined(NO_TLS1) && defined(NO_TLS1_1) && defined(NO_TLS1_2) && !defined(NO_SSL3) ssl3 = 1; #endif #if defined(NO_TLS1) tls1 = 0; #endif @@ -1080,78 +1077,71 @@ dprintf("Called"); if (!proto) { Tcl_AppendResult(interp, "no valid protocol selected", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } /* create SSL context */ -#if defined(NO_SSL2) if (ENABLED(proto, TLS_PROTO_SSL2)) { Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } -#endif -#if defined(NO_SSL3) +#if defined(NO_SSL3) || defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD) if (ENABLED(proto, TLS_PROTO_SSL3)) { Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } #endif -#if defined(NO_TLS1) +#if defined(NO_TLS1) || defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD) if (ENABLED(proto, TLS_PROTO_TLS1)) { Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } #endif -#if defined(NO_TLS1_1) +#if defined(NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD) if (ENABLED(proto, TLS_PROTO_TLS1_1)) { Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } #endif -#if defined(NO_TLS1_2) +#if defined(NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2_METHOD) if (ENABLED(proto, TLS_PROTO_TLS1_2)) { Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } #endif -#if defined(NO_TLS1_3) +#if defined(NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3_METHOD) if (ENABLED(proto, TLS_PROTO_TLS1_3)) { Tcl_AppendResult(interp, "protocol not supported", (char *)NULL); - return (SSL_CTX *)0; + return NULL; } #endif switch (proto) { -#if !defined(NO_SSL2) - case TLS_PROTO_SSL2: - method = SSLv2_method (); - break; -#endif -#if !defined(NO_SSL3) +#if !defined(NO_SSL3) && !defined(OPENSSL_NO_SSL3) && !defined(OPENSSL_NO_SSL3_METHOD) case TLS_PROTO_SSL3: method = SSLv3_method (); break; #endif -#if !defined(NO_TLS1) +#if !defined(NO_TLS1) && !defined(OPENSSL_NO_TLS1) && !defined(OPENSSL_NO_TLS1_METHOD) case TLS_PROTO_TLS1: method = TLSv1_method (); break; #endif -#if !defined(NO_TLS1_1) +#if !defined(NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1_METHOD) case TLS_PROTO_TLS1_1: method = TLSv1_1_method (); break; #endif -#if !defined(NO_TLS1_2) +#if !defined(NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2_METHOD) case TLS_PROTO_TLS1_2: method = TLSv1_2_method (); break; #endif -#if !defined(NO_TLS1_3) +#if !defined(NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3_METHOD) case TLS_PROTO_TLS1_3: /* * The version range is constrained below, * after the context is created. Use the * generic method here. @@ -1163,26 +1153,23 @@ #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) +#if !defined(NO_SSL3) && !defined(OPENSSL_NO_SSL3) && !defined(OPENSSL_NO_SSL3_METHOD) off |= (ENABLED(proto, TLS_PROTO_SSL3) ? 0 : SSL_OP_NO_SSLv3); #endif -#if !defined(NO_TLS1) +#if !defined(NO_TLS1) && !defined(OPENSSL_NO_TLS1) && !defined(OPENSSL_NO_TLS1_METHOD) off |= (ENABLED(proto, TLS_PROTO_TLS1) ? 0 : SSL_OP_NO_TLSv1); #endif -#if !defined(NO_TLS1_1) +#if !defined(NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1_METHOD) off |= (ENABLED(proto, TLS_PROTO_TLS1_1) ? 0 : SSL_OP_NO_TLSv1_1); #endif -#if !defined(NO_TLS1_2) +#if !defined(NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2_METHOD) off |= (ENABLED(proto, TLS_PROTO_TLS1_2) ? 0 : SSL_OP_NO_TLSv1_2); #endif -#if !defined(NO_TLS1_3) +#if !defined(NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3_METHOD) off |= (ENABLED(proto, TLS_PROTO_TLS1_3) ? 0 : SSL_OP_NO_TLSv1_3); #endif break; } @@ -1218,11 +1205,11 @@ #ifdef OPENSSL_NO_DH if (DHparams != NULL) { Tcl_AppendResult(interp, "DH parameter support not available", (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } #else { DH* dh; if (DHparams != NULL) { @@ -1232,21 +1219,21 @@ if (!bio) { Tcl_DStringFree(&ds); Tcl_AppendResult(interp, "Could not find DH parameters file", (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); Tcl_DStringFree(&ds); if (!dh) { Tcl_AppendResult(interp, "Could not read DH parameters from file", (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } } else { dh = get_dhParams(); } SSL_CTX_set_tmp_dh(ctx, dh); @@ -1266,21 +1253,21 @@ Tcl_DStringFree(&ds); Tcl_AppendResult(interp, "unable to set certificate file ", certfile, ": ", REASON(), (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } } else if (cert != NULL) { load_private_key = 1; if (SSL_CTX_use_certificate_ASN1(ctx, cert_len, cert) <= 0) { Tcl_DStringFree(&ds); Tcl_AppendResult(interp, "unable to set certificate: ", REASON(), (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } } else { certfile = (char*)X509_get_default_cert_file(); if (SSL_CTX_use_certificate_file(ctx, certfile, @@ -1289,11 +1276,11 @@ Tcl_DStringFree(&ds); Tcl_AppendResult(interp, "unable to use default certificate file ", certfile, ": ", REASON(), (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; #endif } } /* set our private key */ @@ -1314,11 +1301,11 @@ Tcl_SetResult(interp, NULL, TCL_STATIC); Tcl_AppendResult(interp, "unable to set public key file ", keyfile, " ", REASON(), (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } Tcl_DStringFree(&ds); } else if (key != NULL) { if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, ctx, key,key_len) <= 0) { @@ -1327,21 +1314,21 @@ Tcl_SetResult(interp, NULL, TCL_STATIC); Tcl_AppendResult(interp, "unable to set public key: ", REASON(), (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } } /* Now we know that a key and cert have been set against * the SSL context */ if (!SSL_CTX_check_private_key(ctx)) { Tcl_AppendResult(interp, "private key does not match the certificate public key", (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; } } /* Set verification CAs */ Tcl_DStringInit(&ds); @@ -1353,11 +1340,11 @@ Tcl_DStringFree(&ds1); /* Don't currently care if this fails */ Tcl_AppendResult(interp, "SSL default verify paths: ", REASON(), (char *) NULL); SSL_CTX_free(ctx); - return (SSL_CTX *)0; + return NULL; #endif } /* https://sourceforge.net/p/tls/bugs/57/ */ /* XXX:TODO: Let the user supply values here instead of something that exists on the filesystem */ @@ -1770,11 +1757,11 @@ }; dprintf("Called"); /* - * We only support Tcl 8.4 or newer + * We only support Tcl 8.6 or newer */ if ( #ifdef USE_TCL_STUBS Tcl_InitStubs(interp, "8.6-", 0) #else @@ -1787,17 +1774,17 @@ if (TlsLibInit(0) != TCL_OK) { Tcl_AppendResult(interp, "could not initialize SSL library", (char *)NULL); return TCL_ERROR; } - Tcl_CreateObjCommand(interp, "tls::ciphers", CiphersObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::handshake", HandshakeObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::import", ImportObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::unimport", UnimportObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::status", StatusObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::version", VersionObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::misc", MiscObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "tls::ciphers", CiphersObjCmd, NULL, 0); + Tcl_CreateObjCommand(interp, "tls::handshake", HandshakeObjCmd, NULL, 0); + Tcl_CreateObjCommand(interp, "tls::import", ImportObjCmd, NULL, 0); + Tcl_CreateObjCommand(interp, "tls::unimport", UnimportObjCmd, NULL, 0); + Tcl_CreateObjCommand(interp, "tls::status", StatusObjCmd, NULL, 0); + Tcl_CreateObjCommand(interp, "tls::version", VersionObjCmd, NULL, 0); + Tcl_CreateObjCommand(interp, "tls::misc", MiscObjCmd, NULL, 0); if (interp) { Tcl_Eval(interp, tlsTclInitScript); } @@ -1912,37 +1899,12 @@ SSL_load_error_strings(); ERR_load_crypto_strings(); BIO_new_tcl(NULL, 0); -#if 0 - /* - * XXX:TODO: Remove this code and replace it with a check - * for enough entropy and do not try to create our own - * terrible entropy - */ - /* - * Seed the random number generator in the SSL library, - * using the do/while construct because of the bug note in the - * OpenSSL FAQ at http://www.openssl.org/support/faq.html#USER1 - * - * The crux of the problem is that Solaris 7 does not have a - * /dev/random or /dev/urandom device so it cannot gather enough - * entropy from the RAND_seed() when TLS initializes and refuses - * to go further. Earlier versions of OpenSSL carried on regardless. - */ - srand((unsigned int) time((time_t *) NULL)); - do { - for (i = 0; i < 16; i++) { - rnd_seed[i] = 1 + (char) (255.0 * rand()/(RAND_MAX+1.0)); - } - RAND_seed(rnd_seed, sizeof(rnd_seed)); - } while (RAND_status() != 1); -#endif - done: #if defined(OPENSSL_THREADS) && defined(TCL_THREADS) Tcl_MutexUnlock(&init_mx); #endif return(status); }