0

I have a website, accessible with HTTP over TLS. It is multi-homed, so there are several A DNS entries for the hostname. Additionally, there are several websites, with several hostnames, on the same set of IP addresses, so this is all setup with a few NameVirtualHost in Apache 2.

Accessing the website with a browser, or a WebDAV client, etc works like a charm.

But when I try to connect with "openssl s_client", I cannot for the life of me get it to work. Apache throws a 400 "Bad Request" error at me. Apache selects the correct, so the SNI must have been set correctly by openssl. This happens as soon as I press "enter" on the "GET" line, even before I can add any header. The HTTP syntax I use (basically "GET / HTTP/1.1\nHost: hostname" works on the non-TLS version of the website (which only redirects to the TLS version), so that should not be the problem. The same HTTP syntax also works to connect to e.g. www.google.com over TLS (port 443), so again it looks correct.

Logs of the Apache server show a request with a trailing newline, maybe that's a hint. The log entry goes to the one for the default VirtualHost for that IP/port although Apache sent the X.509 certificate for the hostname I connect to, which is different from the default.

I have checked with wireshark that SNI is set in the ClientHello.

Here's an example session:

INPUT $ openssl s_client -connect hostname:443

OUTPUT

depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = hostname
verify return:1
---
Certificate chain
 0 s:CN = hostname
   i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
 1 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
(...)
-----END CERTIFICATE-----
subject=CN = hostname

issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3121 bytes and written 388 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: FBFD6172E3C659D4071B68A36BD5983A8571906C00190A745B5DAA1359D4087C
    Session-ID-ctx: 
    Resumption PSK: 21957A3C267DAA25CE2D7F6BFD3C3593CCFA1E6FC45C4DC5D0CE35C29175F7E9BBDD59EE6C213DC53B8291B7BD055238
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 18 4b 25 ae 49 61 e8 f3-73 b5 bb 3b 50 2f 1d c4   .K%.Ia..s..;P/..
    0010 - 7e 02 e9 41 01 9e ed 8b-a1 b6 ec 48 4d 12 11 f2   ~..A.......HM...

    Start Time: 1602857943
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 627DF7810556DB2918484F393E66DEA7BF22DD3A42288B47E46FCDCDEF176125
    Session-ID-ctx: 
    Resumption PSK: 851978077C93284CB3D69B541B77844A48D1CFC8800F60B6F7CF3D297911D0D93E0E935017B78777E2BD51A29E13329F
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - c0 1a 60 23 36 80 bf 27-83 0e 1f 26 4b 04 f2 53   ..`#6..'...&K..S
    0010 - a0 8a 2d 6e 41 44 17 c7-9b 11 5d dc 7f 08 71 fc   ..-nAD....]...q.

    Start Time: 1602857943
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK

INPUT (note no blank line after the first one) GET / HTTP/1.1

OUTPUT

Date: Fri, 16 Oct 2020 14:19:11 GMT
Server: Apache/2.4.38 (Debian)
Content-Length: 322
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.38 (Debian) Server at default_hostname Port 443</address>
</body></html>
closed

Note: the error message mentions default_hostname, not hostname!

The corresponding Apache log entry is: default_hostname:443 1.2.3.4 - - [16/Oct/2020:16:19:11 +0200] "GET / HTTP/1.1\n" 400 3279 "-" "-"

Note that default_hostname IS NOT hostname and that Apache sent the correct server certificate, so it has identified the correct VirtualHost! If I connect without SNI (openssl s_client -connect 4.3.2.1:443) then it sends the server certificate for default_hostname.

New contributor
Lionel Elie Mamane is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
0

... "GET / HTTP/1.1\n" 400 3279 "-" "-"

The line ending in a HTTP request should be \r\n and not \n. Try the -crlf option of s_client to translate an Enter into \r\n instead of only \n.

Also the server might have some limit in which time the request should be fully sent and it might not be fast enough to just type everything. Try cut+paste a pre-created request from a text file instead.

| improve this answer | |
  • Yes, the -crlf option did it. Thanks. (Speed of typing was not the issue.) – Lionel Elie Mamane Oct 17 at 18:02
0

You did actually send a malformed request. HTTP/1.1 requires the presence of the Host: header, and further with SNI, the content of the Host: header must match the SNI hostname.

Try again and this time provide the correct Host: header.

| improve this answer | |
  • I cannot add any header. The error comes before I can put any header, as soon as I press "enter" after "HTTP/1.1". AFAIK, there must be a newline between the HTTP request and its headers. I don't mean an empty line! a newline to terminate the request line. The error appears as soon as I add that terminating newline. I apologise if I wasn't clear enough about that in my question, I thought I had explained it. – Lionel Elie Mamane Oct 16 at 15:09
  • @LionelElieMamane That is rather strange. I can't reproduce it here. I don't get a response until I press enter twice. Check your keyboard, your terminal emulator, and maybe your openssl version? – Michael Hampton Oct 16 at 15:15
  • The extra-double strange thing is that I can connect to e.g. www.google.com over TLS just alright with exactly the same setup (same openssl on the same machine with same keyboard and same terminal emulator). I type "GET / HTTP/1.1" then enter, then "Host: www.google.com" then enter twice, works right. Connecting to my Apache 2 on two different machines (different websites), I get the error as soon as I press enter after "GET / HTTP/1.1". – Lionel Elie Mamane Oct 16 at 15:32

Your Answer

Lionel Elie Mamane is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.