@@ -31,13 +31,10 @@ /* Min OpenSSL version */ #if OPENSSL_VERSION_NUMBER < 0x10101000L #error "Only OpenSSL v1.1.1 or later is supported" #endif -/* - * External functions - */ /* * Forward declarations */ @@ -45,12 +42,12 @@ (((key) == NULL) ? (char *)NULL : \ Tcl_TranslateFileName(interp, (key), (dsp))) static SSL_CTX *CTX_Init(State *statePtr, int isServer, int proto, char *key, char *certfile, unsigned char *key_asn1, unsigned char *cert_asn1, - int key_asn1_len, int cert_asn1_len, char *CAdir, char *CAfile, - char *ciphers, char *DHparams); + int key_asn1_len, int cert_asn1_len, char *CApath, char *CAfile, + char *ciphers, char *ciphersuites, int level, char *DHparams); static int TlsLibInit(int uninitialize); #define TLS_PROTO_SSL2 0x01 #define TLS_PROTO_SSL3 0x02 @@ -126,11 +123,11 @@ ret = (unsigned long) Tcl_GetCurrentThread(); dprintf("Returning %lu", ret); - return(ret); + return ret; } #endif /* OPENSSL_THREADS */ #endif /* TCL_THREADS */ @@ -322,11 +319,11 @@ statePtr->flags &= ~(TLS_TCL_CALLBACK); Tcl_Release((void *) statePtr); Tcl_Release((void *) statePtr->interp); - return(ok); /* By default, leave verification unchanged. */ + return ok; /* By default, leave verification unchanged. */ } /* *------------------------------------------------------------------- * @@ -458,10 +455,14 @@ return -1; } } #endif +/********************/ +/* Commands */ +/********************/ + /* *------------------------------------------------------------------- * * CiphersObjCmd -- list available ciphers * @@ -553,10 +554,11 @@ } if (ctx == NULL) { Tcl_AppendResult(interp, GET_ERR_REASON(), (char *)NULL); return TCL_ERROR; } + ssl = SSL_new(ctx); if (ssl == NULL) { Tcl_AppendResult(interp, GET_ERR_REASON(), (char *)NULL); SSL_CTX_free(ctx); return TCL_ERROR; @@ -627,25 +629,25 @@ dprintf("Called"); if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channel"); - return(TCL_ERROR); + return TCL_ERROR; } chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); if (chan == (Tcl_Channel) NULL) { - return(TCL_ERROR); + return TCL_ERROR; } /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); if (Tcl_GetChannelType(chan) != Tls_ChannelType()) { Tcl_AppendResult(interp, "bad channel \"", Tcl_GetChannelName(chan), "\": not a TLS channel", (char *)NULL); Tcl_SetErrorCode(interp, "TLS", "HANDSHAKE", "CHANNEL", "INVALID", (char *)NULL); - return(TCL_ERROR); + return TCL_ERROR; } statePtr = (State *)Tcl_GetChannelInstanceData(chan); dprintf("Calling Tls_WaitForConnect"); ret = Tls_WaitForConnect(statePtr, &err, 1); @@ -668,21 +670,21 @@ if ((result = SSL_get_verify_result(statePtr->ssl)) != X509_V_OK) { Tcl_AppendResult(interp, " due to \"", X509_verify_cert_error_string(result), "\"", (char *)NULL); } Tcl_SetErrorCode(interp, "TLS", "HANDSHAKE", "FAILED", (char *)NULL); dprintf("Returning TCL_ERROR with handshake failed: %s", errStr); - return(TCL_ERROR); + return TCL_ERROR; } else { if (err != 0) { dprintf("Got an error with a completed handshake: err = %i", err); } ret = 1; } dprintf("Returning TCL_OK with data \"%i\"", ret); Tcl_SetObjResult(interp, Tcl_NewIntObj(ret)); - return(TCL_OK); + return TCL_OK; } /* *------------------------------------------------------------------- * @@ -724,33 +726,31 @@ Tcl_Size key_len = 0; unsigned char *cert = NULL; Tcl_Size cert_len = 0; char *ciphers = NULL; char *CAfile = NULL; - char *CAdir = NULL; + char *CApath = NULL; char *DHparams = NULL; 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, tls1_3 = 1; int proto = 0; int verify = 0, require = 0, request = 1; dprintf("Called"); -#if defined(NO_TLS1) +#if defined(NO_TLS1) || defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD) tls1 = 0; #endif -#if defined(NO_TLS1_1) +#if defined(NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD) tls1_1 = 0; #endif -#if defined(NO_TLS1_2) +#if defined(NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2_METHOD) tls1_2 = 0; #endif -#if defined(NO_TLS1_3) +#if defined(NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3) tls1_3 = 0; #endif if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "channel ?options?"); @@ -760,45 +760,41 @@ chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; } - /* - * Make sure to operate on the topmost channel - */ + /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); for (idx = 2; idx < objc; idx++) { char *opt = Tcl_GetString(objv[idx]); if (opt[0] != '-') break; - OPTSTR("-cadir", CAdir); + OPTSTR("-cadir", CApath); OPTSTR("-cafile", CAfile); + OPTBYTE("-cert", cert, cert_len); OPTSTR("-certfile", certfile); OPTSTR("-cipher", ciphers); + OPTSTR("-ciphers", ciphers); OPTOBJ("-command", script); OPTSTR("-dhparams", DHparams); + OPTBYTE("-key", key, key_len); OPTSTR("-keyfile", keyfile); OPTSTR("-model", model); OPTOBJ("-password", password); - OPTBOOL("-require", require); OPTBOOL("-request", request); + OPTBOOL("-require", require); OPTBOOL("-server", server); -#ifndef OPENSSL_NO_TLSEXT - OPTSTR( "-servername", servername); -#endif - + OPTSTR("-servername", servername); OPTBOOL("-ssl2", ssl2); OPTBOOL("-ssl3", ssl3); OPTBOOL("-tls1", tls1); OPTBOOL("-tls1.1", tls1_1); OPTBOOL("-tls1.2", tls1_2); - OPTBOOL("-tls1.3", tls1_3) - OPTBYTE("-cert", cert, cert_len); - OPTBYTE("-key", key, key_len); + OPTBOOL("-tls1.3", tls1_3); OPTBAD("option", "-cadir, -cafile, -cert, -certfile, -cipher, -command, -dhparams, -key, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, or tls1.3"); return TCL_ERROR; } @@ -818,11 +814,11 @@ if (key && !*key) key = NULL; if (certfile && !*certfile) certfile = NULL; if (keyfile && !*keyfile) keyfile = NULL; if (ciphers && !*ciphers) ciphers = NULL; if (CAfile && !*CAfile) CAfile = NULL; - if (CAdir && !*CAdir) CAdir = NULL; + if (CApath && !*CApath) CApath = NULL; if (DHparams && !*DHparams) DHparams = NULL; /* new SSL state */ statePtr = (State *) ckalloc((unsigned) sizeof(State)); memset(statePtr, 0, sizeof(State)); @@ -870,13 +866,12 @@ Tls_Free((void *)statePtr); return TCL_ERROR; } ctx = ((State *)Tcl_GetChannelInstanceData(chan))->ctx; } else { - if ((ctx = CTX_Init(statePtr, server, proto, keyfile, certfile, key, - cert, key_len, cert_len, CAdir, CAfile, ciphers, - DHparams)) == NULL) { + if ((ctx = CTX_Init(statePtr, server, proto, keyfile, certfile, key, cert, key_len, + cert_len, CApath, CAfile, ciphers, NULL, 0, DHparams)) == NULL) { Tls_Free((void *)statePtr); return TCL_ERROR; } } @@ -915,30 +910,30 @@ Tcl_SetChannelOption(interp, statePtr->self, "-blocking", Tcl_DStringValue(&upperChannelBlocking)); /* * SSL Initialization */ - statePtr->ssl = SSL_new(statePtr->ctx); if (!statePtr->ssl) { /* SSL library error */ Tcl_AppendResult(interp, "couldn't construct ssl session: ", GET_ERR_REASON(), (char *)NULL); Tcl_SetErrorCode(interp, "TLS", "IMPORT", "INIT", "FAILED", (char *)NULL); Tls_Free((void *)statePtr); return TCL_ERROR; } -#ifndef OPENSSL_NO_TLSEXT + /* Set host server name */ if (servername) { + /* Sets the server name indication (SNI) in ClientHello extension */ + /* Per RFC 6066, hostname is a ASCII encoded string, though RFC 4366 says UTF-8. */ if (!SSL_set_tlsext_host_name(statePtr->ssl, servername) && require) { Tcl_AppendResult(interp, "setting TLS host name extension failed", (char *)NULL); Tcl_SetErrorCode(interp, "TLS", "IMPORT", "HOSTNAME", "FAILED", (char *)NULL); Tls_Free((void *)statePtr); return TCL_ERROR; } } -#endif /* * SSL Callbacks */ SSL_set_app_data(statePtr->ssl, (void *)statePtr); /* point back to us */ @@ -1001,13 +996,11 @@ chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; } - /* - * Make sure to operate on the topmost channel - */ + /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); if (Tcl_GetChannelType(chan) != Tls_ChannelType()) { Tcl_AppendResult(interp, "bad channel \"", Tcl_GetChannelName(chan), "\": not a TLS channel", (char *)NULL); @@ -1045,13 +1038,15 @@ char *certfile, unsigned char *key, unsigned char *cert, int key_len, int cert_len, - char *CAdir, + char *CApath, char *CAfile, char *ciphers, + TCL_UNUSED(char *), /* ciphersuites */ + TCL_UNUSED(int), /* level */ char *DHparams) { Tcl_Interp *interp = statePtr->interp; SSL_CTX *ctx = NULL; Tcl_DString ds; @@ -1092,11 +1087,11 @@ if (ENABLED(proto, TLS_PROTO_TLS1_2)) { Tcl_AppendResult(interp, "TLS 1.2 protocol not supported", (char *)NULL); return NULL; } #endif -#if defined(NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3_METHOD) +#if defined(NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3) if (ENABLED(proto, TLS_PROTO_TLS1_3)) { Tcl_AppendResult(interp, "TLS 1.3 protocol not supported", (char *)NULL); return NULL; } #endif @@ -1115,17 +1110,18 @@ #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) && !defined(OPENSSL_NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3_METHOD) +#if !defined(NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3) case TLS_PROTO_TLS1_3: /* Use the generic method and constraint range after context is created */ method = TLS_method(); break; #endif default: + /* Negotiate highest available SSL/TLS version */ method = TLS_method(); #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) && !defined(OPENSSL_NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1_METHOD) @@ -1132,19 +1128,19 @@ off |= (ENABLED(proto, TLS_PROTO_TLS1_1) ? 0 : SSL_OP_NO_TLSv1_1); #endif #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) && !defined(OPENSSL_NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3_METHOD) +#if !defined(NO_TLS1_3) && !defined(OPENSSL_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); + return NULL; } #if !defined(NO_TLS1_3) && !defined(OPENSSL_NO_TLS1_3) if (proto == TLS_PROTO_TLS1_3) { SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); @@ -1155,12 +1151,16 @@ SSL_CTX_set_app_data(ctx, interp); /* remember the interpreter */ SSL_CTX_set_options(ctx, SSL_OP_ALL); /* all SSL bug workarounds */ SSL_CTX_set_options(ctx, off); /* disable protocol versions */ SSL_CTX_sess_set_cache_size(ctx, 128); - if (ciphers != NULL) - SSL_CTX_set_cipher_list(ctx, ciphers); + /* Set user defined ciphers, cipher suites, and security level */ + if ((ciphers != NULL) && !SSL_CTX_set_cipher_list(ctx, ciphers)) { + Tcl_AppendResult(interp, "Set ciphers failed: No valid ciphers", (char *)NULL); + SSL_CTX_free(ctx); + return NULL; + } /* set some callbacks */ SSL_CTX_set_default_passwd_cb(ctx, PasswordCallback); #ifndef BSAFE @@ -1279,22 +1279,21 @@ } } /* 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); + Tcl_AppendResult(interp, "private key does not match the certificate public key", + (char *)NULL); SSL_CTX_free(ctx); return NULL; } } /* Set verification CAs */ Tcl_DStringInit(&ds); Tcl_DStringInit(&ds1); - if (!SSL_CTX_load_verify_locations(ctx, F2N(CAfile, &ds), F2N(CAdir, &ds1)) || + if (!SSL_CTX_load_verify_locations(ctx, F2N(CAfile, &ds), F2N(CApath, &ds1)) || !SSL_CTX_set_default_verify_paths(ctx)) { #if 0 Tcl_DStringFree(&ds); Tcl_DStringFree(&ds1); /* Don't currently care if this fails */ @@ -1346,33 +1345,23 @@ char *channelName, *ciphers; int mode; dprintf("Called"); - switch (objc) { - case 2: - channelName = Tcl_GetString(objv[1]); - break; - - case 3: - if (!strcmp (Tcl_GetString (objv[1]), "-local")) { - channelName = Tcl_GetString(objv[2]); - break; - } - /* fallthrough */ - default: - Tcl_WrongNumArgs(interp, 1, objv, "?-local? channel"); - return TCL_ERROR; - } - + if (objc < 2 || objc > 3 || (objc == 3 && !strcmp(Tcl_GetString(objv[1]), "-local"))) { + Tcl_WrongNumArgs(interp, 1, objv, "?-local? channel"); + return TCL_ERROR; + } + + /* Get channel Id */ + channelName = Tcl_GetString(objv[(objc == 2 ? 1 : 2)]); chan = Tcl_GetChannel(interp, channelName, &mode); if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; } - /* - * Make sure to operate on the topmost channel - */ + + /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); if (Tcl_GetChannelType(chan) != Tls_ChannelType()) { Tcl_AppendResult(interp, "bad channel \"", Tcl_GetChannelName(chan), "\": not a TLS channel", (char *)NULL); Tcl_SetErrorCode(interp, "TLS", "STATUS", "CHANNEL", "INVALID", (char *)NULL); @@ -1468,12 +1457,11 @@ if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?args?"); return TCL_ERROR; } - if (Tcl_GetIndexFromObj(interp, objv[1], commands, - "command", 0, &cmd) != TCL_OK) { + if (Tcl_GetIndexFromObj(interp, objv[1], commands, "command", 0, &cmd) != TCL_OK) { return TCL_ERROR; } ERR_clear_error(); @@ -1544,10 +1532,11 @@ Tcl_SetResult(interp,"Unknown parameter",NULL); return TCL_ERROR; } } } + #if OPENSSL_VERSION_NUMBER < 0x30000000L bne = BN_new(); rsa = RSA_new(); pkey = EVP_PKEY_new(); if (bne == NULL || rsa == NULL || pkey == NULL || !BN_set_word(bne,RSA_F4) || @@ -1575,11 +1564,11 @@ Tcl_SetResult(interp,"Error generating certificate request",NULL); EVP_PKEY_free(pkey); #if OPENSSL_VERSION_NUMBER < 0x30000000L BN_free(bne); #endif - return(TCL_ERROR); + return TCL_ERROR; } X509_set_version(cert,2); ASN1_INTEGER_set(X509_get_serialNumber(cert),serial); X509_gmtime_adj(X509_getm_notBefore(cert),0); @@ -1586,17 +1575,17 @@ X509_gmtime_adj(X509_getm_notAfter(cert),(long)60*60*24*days); X509_set_pubkey(cert,pkey); name=X509_get_subject_name(cert); - X509_NAME_add_entry_by_txt(name,"C", MBSTRING_ASC, (unsigned char *) k_C, -1, -1, 0); - X509_NAME_add_entry_by_txt(name,"ST", MBSTRING_ASC, (unsigned char *) k_ST, -1, -1, 0); - X509_NAME_add_entry_by_txt(name,"L", MBSTRING_ASC, (unsigned char *) k_L, -1, -1, 0); - X509_NAME_add_entry_by_txt(name,"O", MBSTRING_ASC, (unsigned char *) k_O, -1, -1, 0); - X509_NAME_add_entry_by_txt(name,"OU", MBSTRING_ASC, (unsigned char *) k_OU, -1, -1, 0); - X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char *) k_CN, -1, -1, 0); - X509_NAME_add_entry_by_txt(name,"Email", MBSTRING_ASC, (unsigned char *) k_Email, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"C", MBSTRING_ASC, (const unsigned char *) k_C, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"ST", MBSTRING_ASC, (const unsigned char *) k_ST, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"L", MBSTRING_ASC, (const unsigned char *) k_L, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"O", MBSTRING_ASC, (const unsigned char *) k_O, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"OU", MBSTRING_ASC, (const unsigned char *) k_OU, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (const unsigned char *) k_CN, -1, -1, 0); + X509_NAME_add_entry_by_txt(name,"Email", MBSTRING_ASC, (const unsigned char *) k_Email, -1, -1, 0); X509_set_subject_name(cert,name); if (!X509_sign(cert,pkey,EVP_sha256())) { X509_free(cert); @@ -1852,11 +1841,11 @@ *------------------------------------------------------* */ DLLEXPORT int Tls_SafeInit(Tcl_Interp *interp) { dprintf("Called"); - return(Tls_Init(interp)); + return Tls_Init(interp); } /* *------------------------------------------------------* * @@ -1883,11 +1872,11 @@ if (uninitialize) { if (!initialized) { dprintf("Asked to uninitialize, but we are not initialized"); - return(TCL_OK); + return TCL_OK; } dprintf("Asked to uninitialize"); #if defined(OPENSSL_THREADS) && defined(TCL_THREADS) @@ -1903,16 +1892,16 @@ #if defined(OPENSSL_THREADS) && defined(TCL_THREADS) Tcl_MutexUnlock(&init_mx); #endif - return(TCL_OK); + return TCL_OK; } if (initialized) { dprintf("Called, but using cached value"); - return(status); + return status; } dprintf("Called"); #if defined(OPENSSL_THREADS) && defined(TCL_THREADS) @@ -1943,7 +1932,7 @@ done: #if defined(OPENSSL_THREADS) && defined(TCL_THREADS) Tcl_MutexUnlock(&init_mx); #endif - return(status); + return status; }