Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -376,18 +376,21 @@ dprintf("Called"); dprintf("VerifyCallback: %d", ok); if (statePtr->vcmd == (Tcl_Obj*)NULL) { + /* Use ok value if verification is required */ if (statePtr->vflags & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { return ok; } else { return 1; } } else if (cert == NULL || ssl == NULL) { return 0; } + + dprintf("VerifyCallback: eval callback"); /* Create command to eval */ cmdPtr = Tcl_DuplicateObj(statePtr->vcmd); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("verify", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, @@ -403,10 +406,12 @@ /* Eval callback command */ Tcl_IncrRefCount(cmdPtr); ok = EvalCallback(interp, statePtr, cmdPtr); Tcl_DecrRefCount(cmdPtr); + + dprintf("VerifyCallback: command result = %d", ok); /* statePtr->flags &= ~(TLS_TCL_CALLBACK); */ return(ok); /* By default, leave verification unchanged. */ } @@ -1192,23 +1197,19 @@ if (ret < 0 && ((statePtr->flags & TLS_TCL_ASYNC) && (err == EAGAIN))) { dprintf("Async set and err = EAGAIN"); ret = 0; } else if (ret < 0) { - long result; errStr = statePtr->err; Tcl_ResetResult(interp); Tcl_SetErrno(err); if (!errStr || (*errStr == 0)) { errStr = Tcl_PosixError(interp); } Tcl_AppendResult(interp, "handshake failed: ", errStr, (char *) NULL); - 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); } else { if (err != 0) { Index: generic/tlsBIO.c ================================================================== --- generic/tlsBIO.c +++ generic/tlsBIO.c @@ -1,13 +1,14 @@ /* * Copyright (C) 1997-2000 Matt Newman * - * Provides BIO layer to interface openssl to Tcl. + * Provides BIO layer to interface OpenSSL to TCL. */ #include "tlsInt.h" +/* Called by SSL_write() */ static int BioWrite(BIO *bio, const char *buf, int bufLen) { Tcl_Channel chan; Tcl_Size ret; int tclEofChan, tclErrno; @@ -19,11 +20,11 @@ tclEofChan = Tcl_Eof(chan); tclErrno = Tcl_GetErrno(); dprintf("[chan=%p] BioWrite(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; tclErrno=%d]", - (void *) chan, bufLen, ret, tclEofChan, Tcl_GetErrno()); + (void *) chan, bufLen, ret, tclEofChan, tclErrno); BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY); if (tclEofChan && ret <= 0) { dprintf("Got EOF while reading, returning a Connection Reset error which maps to Soft EOF"); @@ -43,11 +44,11 @@ } else { dprintf("It's an unexpected error: %s/%i", Tcl_ErrnoMsg(tclErrno), tclErrno); } } else { - dprintf("Successfully wrote some data"); + dprintf("Successfully wrote %" TCL_SIZE_MODIFIER "d bytes of data", ret); } if (ret != -1 || (ret == -1 && tclErrno == EAGAIN)) { if (BIO_should_read(bio)) { dprintf("Setting should retry read flag"); @@ -56,10 +57,11 @@ } } return((int) ret); } +/* Called by SSL_read()*/ static int BioRead(BIO *bio, char *buf, int bufLen) { Tcl_Channel chan; Tcl_Size ret = 0; int tclEofChan, tclErrno; @@ -99,11 +101,11 @@ } else { dprintf("It's an unexpected error: %s/%i", Tcl_ErrnoMsg(tclErrno), tclErrno); } } else { - dprintf("Successfully read some data"); + dprintf("Successfully read %" TCL_SIZE_MODIFIER "d bytes of data", ret); } if (ret != -1 || (ret == -1 && tclErrno == EAGAIN)) { if (BIO_should_write(bio)) { dprintf("Setting should retry write flag"); @@ -119,11 +121,11 @@ } static int BioPuts(BIO *bio, const char *str) { dprintf("BioPuts(%p, ) called", bio, str); - return BioWrite(bio, str, (int) strlen(str)); + return(BioWrite(bio, str, (int) strlen(str))); } static long BioCtrl(BIO *bio, int cmd, long num, void *ptr) { Tcl_Channel chan; long ret = 1; Index: generic/tlsIO.c ================================================================== --- generic/tlsIO.c +++ generic/tlsIO.c @@ -133,14 +133,17 @@ *errorCodePtr = ECONNABORTED; } else { dprintf("Asked to wait for a TLS handshake that has already failed. Returning soft error"); *errorCodePtr = ECONNRESET; } + Tls_Error(statePtr, "Wait for failed handshake"); return(-1); } for (;;) { + ERR_clear_error(); + /* Not initialized yet! Also calls SSL_do_handshake. */ if (statePtr->flags & TLS_TCL_SERVER) { dprintf("Calling SSL_accept()"); err = SSL_accept(statePtr->ssl); @@ -148,22 +151,26 @@ dprintf("Calling SSL_connect()"); err = SSL_connect(statePtr->ssl); } if (err > 0) { - dprintf("That seems to have gone okay"); + dprintf("Accept or connect was successful"); err = BIO_flush(statePtr->bio); if (err <= 0) { dprintf("Flushing the lower layers failed, this will probably terminate this session"); } + } else { + dprintf("Accept or connect failed"); } rc = SSL_get_error(statePtr->ssl, err); - - dprintf("Got error: %i (rc = %i)", err, rc); - dprintf("Got error: %s", ERR_reason_error_string(ERR_get_error())); + backingError = ERR_get_error(); + if (rc != SSL_ERROR_NONE) { + dprintf("Got error: %i (rc = %i)", err, rc); + dprintf("Got error: %s", GET_ERR_REASON()); + } bioShouldRetry = 0; if (err <= 0) { if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT || rc == SSL_ERROR_WANT_READ || rc == SSL_ERROR_WANT_WRITE) { bioShouldRetry = 1; @@ -195,75 +202,78 @@ break; } switch (rc) { case SSL_ERROR_NONE: - /* The connection is up, we are done here */ - dprintf("The connection is up"); + /* The TLS/SSL I/O operation completed */ + dprintf("The connection is good"); *errorCodePtr = 0; break; case SSL_ERROR_ZERO_RETURN: + /* The TLS/SSL peer has closed the connection for writing by sending the close_notify alert */ dprintf("SSL_ERROR_ZERO_RETURN: Connect returned an invalid value..."); *errorCodePtr = EINVAL; + Tls_Error(statePtr, "Peer has closed the connection for writing by sending the close_notify alert"); return(-1); case SSL_ERROR_SYSCALL: - backingError = ERR_get_error(); + /* Some non-recoverable, fatal I/O error occurred */ + dprintf("SSL_ERROR_SYSCALL"); if (backingError == 0 && err == 0) { dprintf("EOF reached") *errorCodePtr = ECONNRESET; + Tls_Error(statePtr, "(unexpected) EOF reached"); + } else if (backingError == 0 && err == -1) { dprintf("I/O error occurred (errno = %lu)", (unsigned long) Tcl_GetErrno()); *errorCodePtr = Tcl_GetErrno(); if (*errorCodePtr == ECONNRESET) { *errorCodePtr = ECONNABORTED; } + Tls_Error(statePtr, Tcl_ErrnoMsg(Tcl_GetErrno())); + } else { dprintf("I/O error occurred (backingError = %lu)", backingError); *errorCodePtr = backingError; if (*errorCodePtr == ECONNRESET) { *errorCodePtr = ECONNABORTED; } + Tls_Error(statePtr, ERR_reason_error_string(backingError)); } statePtr->flags |= TLS_TCL_HANDSHAKE_FAILED; return(-1); case SSL_ERROR_SSL: - dprintf("Got permanent fatal SSL error, aborting immediately"); - Tls_Error(statePtr, (char *)ERR_reason_error_string(ERR_get_error())); + /* A non-recoverable, fatal error in the SSL library occurred, usually a protocol error */ + dprintf("SSL_ERROR_SSL: Got permanent fatal SSL error, aborting immediately"); + if (backingError != 0) { + Tls_Error(statePtr, ERR_reason_error_string(backingError)); + } + if (SSL_get_verify_result(statePtr->ssl) != X509_V_OK) { + Tls_Error(statePtr, X509_verify_cert_error_string(SSL_get_verify_result(statePtr->ssl))); + } statePtr->flags |= TLS_TCL_HANDSHAKE_FAILED; *errorCodePtr = ECONNABORTED; return(-1); case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: - default: - dprintf("We got a confusing reply: %i", rc); - *errorCodePtr = Tcl_GetErrno(); - dprintf("ERR(%d, %d) ", rc, *errorCodePtr); - return(-1); - } - -#if 0 - if (statePtr->flags & TLS_TCL_SERVER) { - dprintf("This is an TLS server, checking the certificate for the peer"); - - err = SSL_get_verify_result(statePtr->ssl); - if (err != X509_V_OK) { - dprintf("Invalid certificate, returning in failure"); - - Tls_Error(statePtr, (char *)X509_verify_cert_error_string(err)); - statePtr->flags |= TLS_TCL_HANDSHAKE_FAILED; - *errorCodePtr = ECONNABORTED; - return(-1); - } - } -#endif + case SSL_ERROR_WANT_ASYNC: + case SSL_ERROR_WANT_ASYNC_JOB: + case SSL_ERROR_WANT_CLIENT_HELLO_CB: + default: + /* The operation did not complete and can be retried later. */ + dprintf("Operation did not complete, call function again later: %i", rc); + *errorCodePtr = EAGAIN; + dprintf("ERR(%d, %d) ", rc, *errorCodePtr); + Tls_Error(statePtr, "Operation did not complete, call function again later"); + return(-1); + } dprintf("Removing the \"TLS_TCL_INIT\" flag since we have completed the handshake"); statePtr->flags &= ~TLS_TCL_INIT; dprintf("Returning in success"); @@ -335,10 +345,11 @@ ERR_clear_error(); bytesRead = BIO_read(statePtr->bio, buf, bufSize); dprintf("BIO_read -> %d", bytesRead); err = SSL_get_error(statePtr->ssl, bytesRead); + backingError = ERR_get_error(); #if 0 if (bytesRead <= 0) { if (BIO_should_retry(statePtr->bio)) { dprintf("I/O failed, will retry based on EAGAIN"); @@ -351,45 +362,55 @@ case SSL_ERROR_NONE: dprintBuffer(buf, bytesRead); break; case SSL_ERROR_SSL: - dprintf("SSL negotiation error, indicating that the connection has been aborted"); - - Tls_Error(statePtr, TCLTLS_SSL_ERROR(statePtr->ssl, bytesRead)); + /* A non-recoverable, fatal error in the SSL library occurred, usually a protocol error */ + dprintf("SSL error, indicating that the connection has been aborted"); + if (backingError != 0) { + Tls_Error(statePtr, ERR_reason_error_string(backingError)); + } *errorCodePtr = ECONNABORTED; bytesRead = -1; break; case SSL_ERROR_SYSCALL: - backingError = ERR_get_error(); + /* Some non-recoverable, fatal I/O error occurred */ if (backingError == 0 && bytesRead == 0) { - dprintf("EOF reached") + /* Unexpected EOF from the peer for OpenSSL 1.1 */ + dprintf("(Unexpected) EOF reached") *errorCodePtr = 0; bytesRead = 0; + Tls_Error(statePtr, "EOF reached"); + } else if (backingError == 0 && bytesRead == -1) { dprintf("I/O error occurred (errno = %lu)", (unsigned long) Tcl_GetErrno()); *errorCodePtr = Tcl_GetErrno(); bytesRead = -1; + Tls_Error(statePtr, Tcl_ErrnoMsg(Tcl_GetErrno())); + } else { dprintf("I/O error occurred (backingError = %lu)", backingError); *errorCodePtr = backingError; bytesRead = -1; + Tls_Error(statePtr, ERR_reason_error_string(backingError)); } break; case SSL_ERROR_ZERO_RETURN: dprintf("Got SSL_ERROR_ZERO_RETURN, this means an EOF has been reached"); bytesRead = 0; *errorCodePtr = 0; + Tls_Error(statePtr, "Peer has closed the connection for writing by sending the close_notify alert"); break; case SSL_ERROR_WANT_READ: dprintf("Got SSL_ERROR_WANT_READ, mapping this to EAGAIN"); bytesRead = -1; *errorCodePtr = EAGAIN; + Tls_Error(statePtr, "SSL_ERROR_WANT_READ"); break; default: dprintf("Unknown error (err = %i), mapping to EOF", err); *errorCodePtr = 0; @@ -486,10 +507,12 @@ ERR_clear_error(); written = BIO_write(statePtr->bio, buf, toWrite); dprintf("BIO_write(%p, %d) -> [%d]", (void *) statePtr, toWrite, written); err = SSL_get_error(statePtr->ssl, written); + backingError = ERR_get_error(); + switch (err) { case SSL_ERROR_NONE: if (written < 0) { written = 0; } @@ -497,52 +520,65 @@ case SSL_ERROR_WANT_WRITE: dprintf("Got SSL_ERROR_WANT_WRITE, mapping it to EAGAIN"); *errorCodePtr = EAGAIN; written = -1; + Tls_Error(statePtr, "SSL_ERROR_WANT_WRITE"); break; case SSL_ERROR_WANT_READ: dprintf(" write R BLOCK"); + Tls_Error(statePtr, "SSL_ERROR_WANT_READ"); break; case SSL_ERROR_WANT_X509_LOOKUP: dprintf(" write X BLOCK"); + Tls_Error(statePtr, "SSL_ERROR_WANT_X509_LOOKUP"); break; case SSL_ERROR_ZERO_RETURN: dprintf(" closed"); written = 0; *errorCodePtr = 0; + Tls_Error(statePtr, "Peer has closed the connection for writing by sending the close_notify alert"); break; case SSL_ERROR_SYSCALL: - backingError = ERR_get_error(); + /* Some non-recoverable, fatal I/O error occurred */ if (backingError == 0 && written == 0) { dprintf("EOF reached") *errorCodePtr = 0; written = 0; + Tls_Error(statePtr, "EOF reached"); + } else if (backingError == 0 && written == -1) { dprintf("I/O error occurred (errno = %lu)", (unsigned long) Tcl_GetErrno()); *errorCodePtr = Tcl_GetErrno(); written = -1; + Tls_Error(statePtr, Tcl_ErrnoMsg(Tcl_GetErrno())); + } else { dprintf("I/O error occurred (backingError = %lu)", backingError); *errorCodePtr = backingError; written = -1; + Tls_Error(statePtr, ERR_reason_error_string(backingError)); } break; case SSL_ERROR_SSL: - Tls_Error(statePtr, TCLTLS_SSL_ERROR(statePtr->ssl, written)); + /* A non-recoverable, fatal error in the SSL library occurred, usually a protocol error */ + dprintf("SSL error, indicating that the connection has been aborted"); + if (backingError != 0) { + Tls_Error(statePtr, ERR_reason_error_string(backingError)); + } *errorCodePtr = ECONNABORTED; written = -1; break; default: - dprintf(" unknown err: %d", err); + dprintf("unknown error: %d", err); break; } if (*errorCodePtr < 0) { Tls_Error(statePtr, strerror(*errorCodePtr)); Index: generic/tlsInt.h ================================================================== --- generic/tlsInt.h +++ generic/tlsInt.h @@ -101,11 +101,10 @@ #define dprintf(...) if (0) { fprintf(stderr, __VA_ARGS__); } #define dprintBuffer(bufferName, bufferLength) /**/ #define dprintFlags(statePtr) /**/ #endif -#define TCLTLS_SSL_ERROR(ssl,err) ((char*)ERR_reason_error_string((unsigned long)SSL_get_error((ssl),(err)))) #define GET_ERR_REASON() ERR_reason_error_string(ERR_get_error()) /* Common list append macros */ #define LAPPEND_BARRAY(interp, obj, text, value, size) {\ if (text != NULL) Tcl_ListObjAppendElement(interp, obj, Tcl_NewStringObj(text, -1)); \