#include <openssl/ocsp.h>
#include "../ssl_locl.h"
+#include "internal/cryptlib.h"
#include "statem_locl.h"
EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt,
}
/*
- * Construct a psk_kex_modes extension. We only have two modes we know about
- * at this stage, so we send both.
+ * Construct a psk_kex_modes extension.
*/
EXT_RETURN tls_construct_ctos_psk_kex_modes(SSL *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx, int *al)
{
#ifndef OPENSSL_NO_TLS1_3
- /*
- * TODO(TLS1.3): Do we want this list to be configurable? For now we always
- * just send both supported modes
- */
+ int nodhe = s->options & SSL_OP_ALLOW_NO_DHE_KEX;
+
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_psk_kex_modes)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_start_sub_packet_u8(pkt)
|| !WPACKET_put_bytes_u8(pkt, TLSEXT_KEX_MODE_KE_DHE)
- || !WPACKET_put_bytes_u8(pkt, TLSEXT_KEX_MODE_KE)
+ || (nodhe && !WPACKET_put_bytes_u8(pkt, TLSEXT_KEX_MODE_KE))
|| !WPACKET_close(pkt)
|| !WPACKET_close(pkt)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
- s->ext.psk_kex_mode = TLSEXT_KEX_MODE_FLAG_KE | TLSEXT_KEX_MODE_FLAG_KE_DHE;
+ s->ext.psk_kex_mode = TLSEXT_KEX_MODE_FLAG_KE_DHE;
+ if (nodhe)
+ s->ext.psk_kex_mode |= TLSEXT_KEX_MODE_FLAG_KE;
#endif
return EXT_RETURN_SENT;
unsigned int context, X509 *x,
size_t chainidx, int *al)
{
+ const unsigned char *id = NULL;
+ size_t idlen = 0;
+ SSL_SESSION *psksess = NULL;
+ SSL_SESSION *edsess = NULL;
+ const EVP_MD *handmd = NULL;
+
+ if (s->hello_retry_request)
+ handmd = ssl_handshake_md(s);
+
+ if (s->psk_use_session_cb != NULL
+ && (!s->psk_use_session_cb(s, handmd, &id, &idlen, &psksess)
+ || (psksess != NULL
+ && psksess->ssl_version != TLS1_3_VERSION))) {
+ SSL_SESSION_free(psksess);
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, SSL_R_BAD_PSK);
+ return EXT_RETURN_FAIL;
+ }
+
+ SSL_SESSION_free(s->psksession);
+ s->psksession = psksess;
+ if (psksess != NULL) {
+ OPENSSL_free(s->psksession_id);
+ s->psksession_id = OPENSSL_memdup(id, idlen);
+ if (s->psksession_id == NULL) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ s->psksession_id_len = idlen;
+ }
+
if (s->early_data_state != SSL_EARLY_DATA_CONNECTING
- || s->session->ext.max_early_data == 0) {
+ || (s->session->ext.max_early_data == 0
+ && (psksess == NULL || psksess->ext.max_early_data == 0))) {
s->max_early_data = 0;
return EXT_RETURN_NOT_SENT;
}
- s->max_early_data = s->session->ext.max_early_data;
+ edsess = s->session->ext.max_early_data != 0 ? s->session : psksess;
+ s->max_early_data = edsess->ext.max_early_data;
+
+ if ((s->ext.hostname == NULL && edsess->ext.hostname != NULL)
+ || (s->ext.hostname != NULL
+ && (edsess->ext.hostname == NULL
+ || strcmp(s->ext.hostname, edsess->ext.hostname) != 0))) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+ SSL_R_INCONSISTENT_EARLY_DATA_SNI);
+ return EXT_RETURN_FAIL;
+ }
+
+ if ((s->ext.alpn == NULL && edsess->ext.alpn_selected != NULL)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+ SSL_R_INCONSISTENT_EARLY_DATA_ALPN);
+ return EXT_RETURN_FAIL;
+ }
+
+ /*
+ * Verify that we are offering an ALPN protocol consistent with the early
+ * data.
+ */
+ if (edsess->ext.alpn_selected != NULL) {
+ PACKET prots, alpnpkt;
+ int found = 0;
+
+ if (!PACKET_buf_init(&prots, s->ext.alpn, s->ext.alpn_len)) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ while (PACKET_get_length_prefixed_1(&prots, &alpnpkt)) {
+ if (PACKET_equal(&alpnpkt, edsess->ext.alpn_selected,
+ edsess->ext.alpn_selected_len)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_EARLY_DATA,
+ SSL_R_INCONSISTENT_EARLY_DATA_ALPN);
+ return EXT_RETURN_FAIL;
+ }
+ }
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data)
|| !WPACKET_start_sub_packet_u16(pkt)
* extension, we set it to accepted.
*/
s->ext.early_data = SSL_EARLY_DATA_REJECTED;
+ s->ext.early_data_ok = 1;
return EXT_RETURN_SENT;
}
/*
* Take off the size of extension header itself (2 bytes for type and
- * 2 bytes for length bytes)
+ * 2 bytes for length bytes), but ensure that the extension is at least
+ * 1 byte long so as not to have an empty extension last (WebSphere 7.x,
+ * 8.x are intolerant of that condition)
*/
if (hlen >= 4)
hlen -= 4;
else
- hlen = 0;
+ hlen = 1;
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_padding)
|| !WPACKET_sub_allocate_bytes_u16(pkt, hlen, &padbytes)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PADDING, ERR_R_INTERNAL_ERROR);
- return 0;
+ return EXT_RETURN_FAIL;
}
memset(padbytes, 0, hlen);
}
X509 *x, size_t chainidx, int *al)
{
#ifndef OPENSSL_NO_TLS1_3
- uint32_t now, agesec, agems;
- size_t reshashsize, pskhashsize, binderoffset, msglen, idlen;
+ uint32_t now, agesec, agems = 0;
+ size_t reshashsize = 0, pskhashsize = 0, binderoffset, msglen;
unsigned char *resbinder = NULL, *pskbinder = NULL, *msgstart = NULL;
- const unsigned char *id;
- const EVP_MD *handmd = NULL, *mdres, *mdpsk;
+ const EVP_MD *handmd = NULL, *mdres = NULL, *mdpsk = NULL;
EXT_RETURN ret = EXT_RETURN_FAIL;
- SSL_SESSION *psksess = NULL;
int dores = 0;
s->session->ext.tick_identity = TLSEXT_PSK_BAD_IDENTITY;
* so don't add this extension.
*/
if (s->session->ssl_version != TLS1_3_VERSION
- || (s->session->ext.ticklen == 0 && s->psk_use_session_cb == NULL))
+ || (s->session->ext.ticklen == 0 && s->psksession == NULL))
return EXT_RETURN_NOT_SENT;
if (s->hello_retry_request)
handmd = ssl_handshake_md(s);
- if (s->psk_use_session_cb != NULL
- && !s->psk_use_session_cb(s, handmd, &id, &idlen, &psksess)) {
- SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, SSL_R_BAD_PSK);
- goto err;
- }
-
if (s->session->ext.ticklen != 0) {
/* Get the digest associated with the ciphersuite in the session */
if (s->session->cipher == NULL) {
}
dopsksess:
- if (!dores && psksess == NULL)
+ if (!dores && s->psksession == NULL)
return EXT_RETURN_NOT_SENT;
- if (psksess != NULL) {
- mdpsk = ssl_md(psksess->cipher->algorithm2);
+ if (s->psksession != NULL) {
+ mdpsk = ssl_md(s->psksession->cipher->algorithm2);
if (mdpsk == NULL) {
/*
* Don't recognize this cipher so we can't use the session.
}
}
- if (psksess != NULL) {
- if (!WPACKET_sub_memcpy_u16(pkt, id, idlen)
+ if (s->psksession != NULL) {
+ if (!WPACKET_sub_memcpy_u16(pkt, s->psksession_id,
+ s->psksession_id_len)
|| !WPACKET_put_bytes_u32(pkt, 0)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
goto err;
|| !WPACKET_start_sub_packet_u16(pkt)
|| (dores
&& !WPACKET_sub_allocate_bytes_u8(pkt, reshashsize, &resbinder))
- || (psksess != NULL
+ || (s->psksession != NULL
&& !WPACKET_sub_allocate_bytes_u8(pkt, pskhashsize, &pskbinder))
|| !WPACKET_close(pkt)
|| !WPACKET_close(pkt)
goto err;
}
- if (psksess != NULL
+ if (s->psksession != NULL
&& tls_psk_do_binder(s, mdpsk, msgstart, binderoffset, NULL,
- pskbinder, psksess, 1, 1) != 1) {
+ pskbinder, s->psksession, 1, 1) != 1) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_PSK, ERR_R_INTERNAL_ERROR);
goto err;
}
if (dores)
s->session->ext.tick_identity = 0;
- SSL_SESSION_free(s->psksession);
- s->psksession = psksess;
- if (psksess != NULL)
+ if (s->psksession != NULL)
s->psksession->ext.tick_identity = (dores ? 1 : 0);
- psksess = NULL;
ret = EXT_RETURN_SENT;
err:
- SSL_SESSION_free(psksess);
return ret;
#else
- return 1;
+ return EXT_RETURN_NOT_SENT;
#endif
}
}
s->s3->alpn_selected_len = len;
+ if (s->session->ext.alpn_selected == NULL
+ || s->session->ext.alpn_selected_len != len
+ || memcmp(s->session->ext.alpn_selected, s->s3->alpn_selected, len)
+ != 0) {
+ /* ALPN not consistent with the old session so cannot use early_data */
+ s->ext.early_data_ok = 0;
+ }
+ if (!s->hit) {
+ /* If a new session then update it with the selected ALPN */
+ s->session->ext.alpn_selected =
+ OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
+ if (s->session->ext.alpn_selected == NULL) {
+ *al = SSL_AD_INTERNAL_ERROR;
+ return 0;
+ }
+ s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
+ }
+
return 1;
}
return 0;
}
- if (s->ext.early_data != SSL_EARLY_DATA_REJECTED
+ if (!s->ext.early_data_ok
|| !s->hit
|| s->session->ext.tick_identity != 0) {
/*
* If we get here then we didn't send early data, or we didn't resume
- * using the first identity so the server should not be accepting it.
+ * using the first identity, or the SNI/ALPN is not consistent so the
+ * server should not be accepting it.
*/
*al = SSL_AD_ILLEGAL_PARAMETER;
return 0;
return 0;
}
+ /*
+ * If we used the external PSK for sending early_data then s->early_secret
+ * is already set up, so don't overwrite it. Otherwise we copy the
+ * early_secret across that we generated earlier.
+ */
+ if ((s->early_data_state != SSL_EARLY_DATA_WRITE_RETRY
+ && s->early_data_state != SSL_EARLY_DATA_FINISHED_WRITING)
+ || s->session->ext.max_early_data > 0
+ || s->psksession->ext.max_early_data == 0)
+ memcpy(s->early_secret, s->psksession->early_secret, EVP_MAX_MD_SIZE);
+
SSL_SESSION_free(s->session);
s->session = s->psksession;
s->psksession = NULL;
- memcpy(s->early_secret, s->session->early_secret, EVP_MAX_MD_SIZE);
s->hit = 1;
#endif