openssl: OpenSSL doesn't properly encode NewSessionTicket when using SSL_CTX_set_tlsext_ticket_key_cb callback that returns 0
I’m working with OpenSSL 1.1.1q.
We have our own build of OpenSSL.
We have custom session ticket handling logic that’s configured through SSL_CTX_set_tlsext_ticket_key_cb. On the server, this callback logic can sometimes return 0 which
indicates that it was not possible to set/retrieve a session ticket and the SSL/TLS session will continue by negotiating a set of cryptographic parameters or using the alternate SSL/TLS resumption mechanism, session ids.
We recently enabled TLS 1.3 on this service. Clients, also on TLS 1.3, started seeing new session ticket processing errors like
error:1416E09F:SSL routines:tls_process_new_session_ticket:length mismatch
This only occurs when the callback from SSL_CTX_set_tlsext_ticket_key_cb returns 0.
Here’s a server repro (that I’ve adapted from https://wiki.openssl.org/index.php/Simple_TLS_Server). You’ll need to plug in your own cert/key paths.
#include <arpa/inet.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int create_socket(int port) {
int s;
struct sockaddr_in6 addr;
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
addr.sin6_addr = in6addr_any;
s = socket(AF_INET6, SOCK_STREAM, 0);
if (s < 0) {
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("Unable to bind");
exit(EXIT_FAILURE);
}
if (listen(s, 1) < 0) {
perror("Unable to listen");
exit(EXIT_FAILURE);
}
return s;
}
SSL_CTX* create_context() {
const SSL_METHOD* method;
SSL_CTX* ctx;
method = TLS_server_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
int handle_session_ticket(
SSL* s,
unsigned char key_name[16],
unsigned char iv[EVP_MAX_IV_LENGTH],
EVP_CIPHER_CTX* ctx,
HMAC_CTX* hctx,
int enc /* when set to 1 */) {
return 0;
}
void configure_context(SSL_CTX* ctx) {
if (SSL_CTX_use_certificate_file(ctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
SSL_CTX_set_session_cache_mode(
ctx,
SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL |
SSL_SESS_CACHE_NO_AUTO_CLEAR);
SSL_CTX_set_tlsext_ticket_key_cb(ctx, handle_session_ticket);
}
int main(int argc, char** argv) {
int sock;
SSL_CTX* ctx;
ctx = create_context();
configure_context(ctx);
sock = create_socket(4433);
while (1) {
struct sockaddr_in addr;
unsigned int len = sizeof(addr);
SSL* ssl;
const char reply[] = "test\n";
int client = accept(sock, (struct sockaddr*)&addr, &len);
if (client < 0) {
perror("Unable to accept");
exit(EXIT_FAILURE);
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) <= 0) {
ERR_print_errors_fp(stderr);
} else {
SSL_write(ssl, reply, strlen(reply));
}
SSL_shutdown(ssl);
SSL_free(ssl);
close(client);
}
close(sock);
SSL_CTX_free(ctx);
}
Once this is running, we can use openssl s_client to repro the client side:
> openssl version
OpenSSL 1.1.1k FIPS 25 Mar 2021
> openssl s_client -connect [::1]:4433 -msg
[...]
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
[...]
Verify return code: 0 (ok)
---
<<< ??? [length 0005]
17 03 03 00 1d
<<< TLS 1.3 [length 0001]
16
<<< TLS 1.3, Handshake [length 000c], NewSessionTicket
04 00 00 08 00 00 00 00 00 00 00 00
>>> ??? [length 0005]
17 03 03 00 13
>>> TLS 1.3 [length 0001]
15
>>> TLS 1.3, Alert [length 0002], fatal decode_error
02 32
139758528358208:error:1416E09F:SSL routines:tls_process_new_session_ticket:length mismatch:ssl/statem/statem_clnt.c:2592:
If my understanding is correct, in the messages above, the 16 indicates the handshake ContentType, 4 indicates the new_session_ticket HandshakeType, the 00 00 08 are the Handshake message’s length.
The following bytes are the NewSessionTicket message. The first 6 bytes might be the encoding from the statem_srvr.c (which also doesn’t seem to match the uint32 ticket_lifetime; and uint32 ticket_age_add; definitions). I don’t know what the final two are, maybe extensions? In any case, I think the client is expecting bytes for the ticket_nonce and/or ticket fields and that’s why it fails while decoding.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 17 (13 by maintainers)
Commits related to this issue
- If a ticket key callback returns 0 in TLSv1.3 don't send a ticket If we can't construct the ticket don't send one. This requires a change to the TLS state machine to be able to a handle a constructio... — committed to mattcaswell/openssl by mattcaswell 2 years ago
- If a ticket key callback returns 0 in TLSv1.3 don't send a ticket If we can't construct the ticket don't send one. This requires a change to the TLS state machine to be able to a handle a constructio... — committed to mattcaswell/openssl by mattcaswell 2 years ago
- If a ticket key callback returns 0 in TLSv1.3 don't send a ticket If we can't construct the ticket don't send one. This requires a change to the TLS state machine to be able to a handle a constructio... — committed to mattcaswell/openssl by mattcaswell 2 years ago
- If a ticket key callback returns 0 in TLSv1.3 don't send a ticket If we can't construct the ticket don't send one. This requires a change to the TLS state machine to be able to a handle a constructio... — committed to sftcd/openssl by mattcaswell 2 years ago
- If a ticket key callback returns 0 in TLSv1.3 don't send a ticket If we can't construct the ticket don't send one. This requires a change to the TLS state machine to be able to a handle a constructio... — committed to openssl/openssl by mattcaswell 2 years ago
- Use a fallback ticket key when no seeds are configured Summary: OpenSSL has not backported its fix from https://github.com/openssl/openssl/issues/18977#issuecomment-1211675124 to 1.1.1, our current v... — committed to facebook/wangle by deleted user 2 years ago
- Use a fallback ticket key when no seeds are configured Summary: OpenSSL has not backported its fix from https://github.com/openssl/openssl/issues/18977#issuecomment-1211675124 to 1.1.1, our current v... — committed to facebook/hhvm by deleted user 2 years ago
- If a ticket key callback returns 0 in TLSv1.3 don't send a ticket If we can't construct the ticket don't send one. This requires a change to the TLS state machine to be able to a handle a constructio... — committed to beldmit/openssl by mattcaswell 2 years ago
Unfortunately 1.1.1 is in security-fix only mode so the fix won’t be backported there.
Thanks for the reproducer. I can replicate this in master too.