Golang: tls.Handshake hangs after connection upgrade - ssl

I am writing a TCP text protocol for a project that I am working on. One of the commands in the protocol is STARTTLS which should upgrade the connection to TLS and continue on. My code to upgrade the connection is similar to the answer in this question. The problem I am having is when I am upgrading the TLS connection, the tlsConn.Handshake will hang and never let go. There are some code samples below. Any help is greatly appreciated.
After the STARTTLS command is received...
// Init a new TLS connection. I need a *tls.Conn type
// so that I can do the Handshake()
s.Logf("++> Upgrading connection to TLS")
tlsConn := tls.Server(s.Conn, s.Server.TLSConfig)
s.Logf("++> Attempting TLS Handshake")
tlsConn.Handshake()
s.Logf("++> TLS Handshake Successful")
// Here is the trick. Since I do not need to access
// any of the TLS functions anymore,
// I can convert tlsConn back in to a net.Conn type
s.Conn = net.Conn(tlsConn)
s.Logf("++> Updating read/write buffers")
s.reader = textproto.NewReader(bufio.NewReader(s.Conn))
s.writer = textproto.NewWriter(bufio.NewWriter(s.Conn))
s.Printf("100 SUCCESS")
The client is currently upgrading the connection right after it sends the STARTTLS command like this...
c.conn = tls.Client(c.conn, clientTLSConfig)
The server *tls.Config looks like this...
// Load the key and certificate - paths are provided in flags.
cert, err := tls.LoadX509KeyPair(flagTLSCert, flagTLSKey)
if err != nil {
log.Fatal(err)
}
// Create the TLS config
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.VerifyClientCertIfGiven,
ServerName: fqdn(),
}
The client *tls.Config looks like this...
clientTLSConfig := &tls.Config{
InsecureSkipVerify: true,
}

Do you call c.conn.Handshake() or do something else to initiate the TLS handshake on the client side?
If the client does not initiate the handshake by sending TLS Client Hello, the server will sit forever waiting for it.
This is my best guess as you did not provide much of the client side code. Also checking with tcpdump would help to narrow the problem down (to server or client side).

Related

In non-blocking mode openssl allows to use any certificate on client side

I changed BIO to a non-blocking mode with
BIO_set_nbio(m_bio, 1)
for BIO_do_connect to not hang (and using BIO_should_retry and select to retry to reconnect). It solved my problem with connection to the wrong listener now fails immediately instead of timing out in 2 hours.
But now I have a new problem - SSL_get_verify_result always returns X509_V_OK. Doesn't matter if it is expired cert or just not the same as server cert - validation always succeeds.
What I don't understand is how and why non-blocking mode changes validation for a cert. I confirmed that without switching to a non-blocking mode validation fails for if the client cert not the same.
I tried to set client cert with both SSL_CTX_load_verify_locations and SSL_CTX_use_certificate_file and it doesn't seem to matter.
As Patrick pointed out the issue I had was due to SSL context in non-blocking mode didn't have server cert so verification immediately after establishing a connection always succeeded. To fix the issue I added cert validation (if it is the same I expect to see) after first read-write data exchange.
SSL_CTX_new(SSLv23_client_method());
SSL_CTX_load_verify_locations(ctx, certFilePath, NULL);
BIO_new_ssl_connect(ctx);
BIO_get_ssl(bio, &ssl);
BIO_set_conn_hostname(bio, hostname);
BIO_set_nbio(bio, 1);
auto err = BIO_do_connect(bio);
// check err and BIO_should_retry to retry as needed
// Problem I had was here - the call always returns X509_V_OK
// in non-blocking mode regardless of what cert I use on
// my (client) side.
// In IO blocking mode (without BIO_set_nbio call above) validation
// fails as expected for mismatching certs.
SSL_get_verify_result(ssl);
// Exchange some data with server with
// BIO_read/BIO_write
// And now we have server cert in ctx
auto clientCert = SSL_get_certificate(ssl);
auto serverCert = SSL_get_peer_certificate(ssl);
auto certsAreTheSame = X509_cmp(clientCert, serverCert) == 0;

ACE SSL Error: peer did not return a certificate

I am making both server and client for an application, using the ACE library with OpenSSL. I am trying to get mutual authentication to work, o the server will only accept connections from trusted clients.
I have generated a CA key and cert, and used it to sign a server cert and a client cert (each with their own keys also). I seem to be loading the trusted store correctly, but I keep getting the error "peer did not return a certificate" during handshake.
Server side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_server);
context->certificate("../ACE-server/server_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-server/server_key.pem", SSL_FILETYPE_PEM);
if (context->load_trusted_ca("../ACE-server/trusted.pem", 0, false) == -1) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "load_trusted_ca"), -1);
}
if (context->have_trusted_ca() <= 0) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "have_trusted_ca"), -1);
}
Client side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_client);
context->certificate("../ACE-client/client_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-client/client_key.pem", SSL_FILETYPE_PEM);
I generated the certificates following these instructions: https://blog.codeship.com/how-to-set-up-mutual-tls-authentication/
And checking online, I found that if the .crt and .key files are readable, they should already be in .pem format and there is no need to convert them. So I just changed the extension and used them here.
Any help is appreciated!
My problem apparently was the same as seen here: OpenSSL client not sending client certificate
I was changing the SSL context after creating the SSL Socket. Now the mutual authentication works, but my client crashes when closing the connection. Though I don't know why that is yet.

tls: oversized record received with length XXXXX

I use the built-in standard SSL socket client library (net + crypto/tls) like this:
conn, err := net.Dial("tcp", "exploit.im:5222")
//...
config := tls.Config{InsecureSkipVerify: true}
tls_conn := tls.Client(conn, &config)
fmt.Println(tls_conn.Handshake())
And am getting the message:
conn, err := net.Dial("tcp", "exploit.im:5222")
I managed to find out it is somehow related to the default maximum packet size (16384 + 2048 set in common.go:31). Is there any standard work around (without patching this value & rebuilding the lib)?
You get this kind of messages if you try to do a SSL handshake with a peer which does not reply with SSL. In this case it is probably some XMPP server and with XMPP you first have some clear text handshake before you start with SSL. Trying to start directly with SSL will result in interpreting the servers clear text response as an SSL frame which can result in strange error messages like this.

Get net/http: TLS handshake timeout golang

Need your help with this
package main
import (
"log"
"net/http"
)
func main() {
client := &http.Client{}
_, err := client.Get("https://www.marathonbet.com/en/")
if err != nil {
log.Fatalf("%s\n", err)
}
}
This always return:Get https://www.marathonbet.com/en/: net/http: TLS handshake timeout exit status 1
I`m try:
it
and use this lib
and do it
But nothing works for me..
So, please help me.
Update:
In Python 2.7 with requests this works:
s = Session()
r = s.get('https://www.marathonbet.com/en/, verify=False)
But i need do it with go(
UPD:
Fixed: Just replase https://www.marathonbet.com/en/ to https://87.117.250.213/en/ and adding skip verify.
thx all for help.
Here's a more detailed explanation of what's happening. When you call a domain, your HTTP client calls a DNS server. The DNS server responds with the IP of the target server. At this point everything's OK.
If it's an HTTPS connection, then it starts the TLS handshake. Here's how it works.
And this the point where you experienced the issue. The request was sent but the server didn't answer correctly (or at all). It may be caused by many factors like the server:
isn't accessible
needs more time to respond
can be hidden behind some firewall/proxy that refuses the connection
block all requests from your IP/location
etc
By providing the skip verify option and providing the explicit IP address, you skips everything I described above. It means:
if the server's IP changes your code will stop working
if someone perform a man-in-the-middle attach you won't find out about it.
It's hard to find out what's the root cause without a more deep investigation. If you want to find out what's happening, use the httptrace as #Flowchartsman suggested

How to handle SSL handshake failure in Netty

We're implementing SSL on top of Netty. But there's a flaw in current design. The client will retry connecting to server if failed. This is desired in Network or heavy server load issues. But wrong client credentials would cause constant failure.
There're some solutions:
The client-server connection can failover to unencrypted mode
(removing SslHandler from pipeline).
Client can die and throw exception on knowing it's SSL exception.
Unfortunately I don't know how to implement this using Netty. A few questions:
How to detect it's SSL exception?
How to remove the SslHandler from both side safely?
Please help out here.
As far as I know, there's no safe way to downgrade an SSL connection to a plaintext connection.
To detect a handshake failure, you need to implement userEventTriggered() handler method:
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof SslHandshakeCompletionEvent) {
if (!((SslHandshakeCompletionEvent) evt).isSuccess()) {
// handshake failed
}
}
}
You can also add a listener to the handshake future:
ChannelPipeline p = ...;
SslHandler sslHandler = p.get(SslHandler.class);
sslHandler.handshakeFuture().addListener(new FutureListener<Channel> { ... });