diff options
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/ssl_conf.c | 7 | ||||
-rw-r--r-- | ssl/ssl_err.c | 19 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 54 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 22 | ||||
-rw-r--r-- | ssl/statem/extensions.c | 17 | ||||
-rw-r--r-- | ssl/statem/extensions_clnt.c | 42 | ||||
-rw-r--r-- | ssl/statem/extensions_cust.c | 1 | ||||
-rw-r--r-- | ssl/statem/extensions_srvr.c | 14 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 75 | ||||
-rw-r--r-- | ssl/statem/statem_lib.c | 111 | ||||
-rw-r--r-- | ssl/statem/statem_locl.h | 10 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 121 | ||||
-rw-r--r-- | ssl/t1_trce.c | 3 |
13 files changed, 443 insertions, 53 deletions
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c index 0cd8ace437..cb4ff8daad 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -386,7 +386,12 @@ static int cmd_VerifyMode(SSL_CONF_CTX *cctx, const char *value) SSL_FLAG_VFY_SRV("Request", SSL_VERIFY_PEER), SSL_FLAG_VFY_SRV("Require", SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT), - SSL_FLAG_VFY_SRV("Once", SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE) + SSL_FLAG_VFY_SRV("Once", SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE), + SSL_FLAG_VFY_SRV("RequestPostHandshake", + SSL_VERIFY_PEER | SSL_VERIFY_POST_HANDSHAKE), + SSL_FLAG_VFY_SRV("RequirePostHandshake", + SSL_VERIFY_PEER | SSL_VERIFY_POST_HANDSHAKE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT), }; if (value == NULL) return -3; diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 746678cb39..f0bde60994 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -351,6 +351,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_VALIDATE_CT, 0), "ssl_validate_ct"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_VERIFY_CERT_CHAIN, 0), "ssl_verify_cert_chain"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, 0), + "SSL_verify_client_post_handshake"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE, 0), "SSL_write"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EARLY_DATA, 0), "SSL_write_early_data"}, @@ -369,6 +371,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_GENERATE_SECRET, 0), "tls13_generate_secret"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_HKDF_EXPAND, 0), "tls13_hkdf_expand"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA, 0), + "tls13_restore_handshake_digest_for_pha"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA, 0), + "tls13_save_handshake_digest_for_pha"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS13_SETUP_KEY_BLOCK, 0), "tls13_setup_key_block"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS1_CHANGE_CIPHER_STATE, 0), @@ -441,6 +447,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_ctos_npn"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PADDING, 0), "tls_construct_ctos_padding"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH, 0), + "tls_construct_ctos_post_handshake_auth"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK, 0), "tls_construct_ctos_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, 0), @@ -557,6 +565,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_parse_ctos_key_share"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_MAXFRAGMENTLEN, 0), "tls_parse_ctos_maxfragmentlen"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH, 0), + "tls_parse_ctos_post_handshake_auth"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK, 0), "tls_parse_ctos_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES, 0), "tls_parse_ctos_psk_kex_modes"}, @@ -832,6 +842,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "exceeds max fragment size"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXCESSIVE_MESSAGE_SIZE), "excessive message size"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTENSION_NOT_RECEIVED), + "extension not received"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTRA_DATA_IN_MESSAGE), "extra data in message"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXT_LENGTH_MISMATCH), @@ -868,8 +880,10 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "invalid command"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMPRESSION_ALGORITHM), "invalid compression algorithm"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONFIG), "invalid config"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONFIGURATION_NAME), "invalid configuration name"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONTEXT), "invalid context"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CT_VALIDATION_TYPE), "invalid ct validation type"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_KEY_UPDATE_TYPE), @@ -919,6 +933,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "missing tmp ecdh key"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_ON_RECORD_BOUNDARY), "not on record boundary"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_SERVER), "not server"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_APPLICATION_PROTOCOL), "no application protocol"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATES_RETURNED), @@ -978,6 +993,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "pem name bad prefix"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_TOO_SHORT), "pem name too short"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PIPELINE_FAILURE), "pipeline failure"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR), + "post handshake auth encoding err"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROTOCOL_IS_SHUTDOWN), "protocol is shutdown"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_IDENTITY_NOT_FOUND), @@ -996,6 +1013,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "renegotiation encoding err"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_MISMATCH), "renegotiation mismatch"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUEST_PENDING), "request pending"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUEST_SENT), "request sent"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_CIPHER_MISSING), "required cipher missing"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 270d4de593..5a5fbad1f6 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1186,6 +1186,8 @@ void SSL_free(SSL *s) OPENSSL_free(s->ext.alpn); OPENSSL_free(s->ext.tls13_cookie); OPENSSL_free(s->clienthello); + OPENSSL_free(s->pha_context); + EVP_MD_CTX_free(s->pha_dgst); sk_X509_NAME_pop_free(s->ca_names, X509_NAME_free); @@ -5318,3 +5320,55 @@ int SSL_stateless(SSL *s) return 0; } + +void SSL_force_post_handshake_auth(SSL *ssl) +{ + ssl->pha_forced = 1; +} + +int SSL_verify_client_post_handshake(SSL *ssl) +{ + if (!SSL_IS_TLS13(ssl)) { + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_WRONG_SSL_VERSION); + return 0; + } + if (!ssl->server) { + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_NOT_SERVER); + return 0; + } + + if (!SSL_is_init_finished(ssl)) { + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_STILL_IN_INIT); + return 0; + } + + switch (ssl->post_handshake_auth) { + case SSL_PHA_NONE: + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_EXTENSION_NOT_RECEIVED); + return 0; + default: + case SSL_PHA_EXT_SENT: + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, ERR_R_INTERNAL_ERROR); + return 0; + case SSL_PHA_EXT_RECEIVED: + break; + case SSL_PHA_REQUEST_PENDING: + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_REQUEST_PENDING); + return 0; + case SSL_PHA_REQUESTED: + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_REQUEST_SENT); + return 0; + } + + ssl->post_handshake_auth = SSL_PHA_REQUEST_PENDING; + + /* checks verify_mode and algorithm_auth */ + if (!send_certificate_request(ssl)) { + ssl->post_handshake_auth = SSL_PHA_EXT_RECEIVED; /* restore on error */ + SSLerr(SSL_F_SSL_VERIFY_CLIENT_POST_HANDSHAKE, SSL_R_INVALID_CONFIG); + return 0; + } + + ossl_statem_set_in_init(ssl, 1); + return 1; +} diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 6afd0091cf..221d5b903a 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -402,6 +402,15 @@ #define CERT_PRIVATE_KEY 2 */ +/* Post-Handshake Authentication state */ +typedef enum { + SSL_PHA_NONE = 0, + SSL_PHA_EXT_SENT, /* client-side only: extension sent */ + SSL_PHA_EXT_RECEIVED, /* server-side only: extension received */ + SSL_PHA_REQUEST_PENDING, /* server-side only: request pending */ + SSL_PHA_REQUESTED /* request received by client, or sent by server */ +} SSL_PHA_STATE; + /* CipherSuite length. SSLv3 and all TLS versions. */ # define TLS_CIPHER_LEN 2 /* used to hold info on the particular ciphers used */ @@ -702,6 +711,7 @@ typedef enum tlsext_index_en { TLSEXT_IDX_signed_certificate_timestamp, TLSEXT_IDX_extended_master_secret, TLSEXT_IDX_signature_algorithms_cert, + TLSEXT_IDX_post_handshake_auth, TLSEXT_IDX_signature_algorithms, TLSEXT_IDX_supported_versions, TLSEXT_IDX_psk_kex_modes, @@ -1334,6 +1344,14 @@ struct ssl_st { int renegotiate; /* If sending a KeyUpdate is pending */ int key_update; + /* Post-handshake authentication state */ + SSL_PHA_STATE post_handshake_auth; + int pha_forced; + uint8_t* pha_context; + size_t pha_context_len; + int certreqs_sent; + EVP_MD_CTX *pha_dgst; /* this is just the digest through ClientFinished */ + # ifndef OPENSSL_NO_SRP /* ctx for SRP authentication */ SRP_CTX srp_ctx; @@ -2535,6 +2553,10 @@ __owur int srp_generate_server_master_secret(SSL *s); __owur int srp_generate_client_master_secret(SSL *s); __owur int srp_verify_server_param(SSL *s); +/* statem/statem_srvr.c */ + +__owur int send_certificate_request(SSL *s); + /* statem/extensions_cust.c */ custom_ext_method *custom_ext_find(const custom_ext_methods *exts, diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index 5ad86f20af..2faba13fd0 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -56,6 +56,8 @@ static int final_sig_algs(SSL *s, unsigned int context, int sent); static int final_early_data(SSL *s, unsigned int context, int sent); static int final_maxfragmentlen(SSL *s, unsigned int context, int sent); +static int init_post_handshake_auth(SSL *s, unsigned int context); + /* Structure to define a built-in extension */ typedef struct extensions_definition_st { /* The defined type for the extension */ @@ -290,6 +292,14 @@ static const EXTENSION_DEFINITION ext_defs[] = { NULL, NULL, NULL }, { + TLSEXT_TYPE_post_handshake_auth, + SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ONLY, + init_post_handshake_auth, + tls_parse_ctos_post_handshake_auth, NULL, + NULL, tls_construct_ctos_post_handshake_auth, + NULL, + }, + { TLSEXT_TYPE_signature_algorithms, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST, init_sig_algs, tls_parse_ctos_sig_algs, @@ -1653,3 +1663,10 @@ static int final_maxfragmentlen(SSL *s, unsigned int context, int sent) return 1; } + +static int init_post_handshake_auth(SSL *s, unsigned int context) +{ + s->post_handshake_auth = SSL_PHA_NONE; + + return 1; +} diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index 5441e98267..6286242ad5 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -1133,6 +1133,48 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context, #endif } +EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, + unsigned int context, + X509 *x, size_t chainidx) +{ +#ifndef OPENSSL_NO_TLS1_3 + if (!s->pha_forced) { + int i, n = 0; + + /* check for cert, if present, we can do post-handshake auth */ + if (s->cert == NULL) + return EXT_RETURN_NOT_SENT; + + for (i = 0; i < SSL_PKEY_NUM; i++) { + if (s->cert->pkeys[i].x509 != NULL + && s->cert->pkeys[i].privatekey != NULL) + n++; + } + + /* no identity certificates, so no extension */ + if (n == 0) + return EXT_RETURN_NOT_SENT; + } + + /* construct extension - 0 length, no contents */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_post_handshake_auth) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH, + ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + s->post_handshake_auth = SSL_PHA_EXT_SENT; + + return EXT_RETURN_SENT; +#else + return EXT_RETURN_NOT_SENT; +#endif +} + + /* * Parse the server's renegotiation binding and abort if it's not right */ diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c index 0a23630cf2..60a7c37bb1 100644 --- a/ssl/statem/extensions_cust.c +++ b/ssl/statem/extensions_cust.c @@ -525,6 +525,7 @@ int SSL_extension_supported(unsigned int ext_type) case TLSEXT_TYPE_early_data: case TLSEXT_TYPE_certificate_authorities: case TLSEXT_TYPE_psk: + case TLSEXT_TYPE_post_handshake_auth: return 1; default: return 0; diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 0a7bac4d8b..27ff5a5773 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -1161,6 +1161,20 @@ err: return 0; } +int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + if (PACKET_remaining(pkt) != 0) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH, + SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR); + return 0; + } + + s->post_handshake_auth = SSL_PHA_EXT_RECEIVED; + + return 1; +} + /* * Add the server's renegotiation binding */ diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index e79bd7b9c0..5050233866 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved * Copyright 2005 Nokia. All rights reserved. * @@ -160,6 +160,26 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt) st->hand_state = TLS_ST_CR_KEY_UPDATE; return 1; } + if (mt == SSL3_MT_CERTIFICATE_REQUEST) { +#if DTLS_MAX_VERSION != DTLS1_2_VERSION +# error TODO(DTLS1.3): Restore digest for PHA before adding message. +#endif + if (!SSL_IS_DTLS(s) && s->post_handshake_auth == SSL_PHA_EXT_SENT) { + s->post_handshake_auth = SSL_PHA_REQUESTED; + /* + * In TLS, this is called before the message is added to the + * digest. In DTLS, this is expected to be called after adding + * to the digest. Either move the digest restore, or add the + * message here after the swap, or do it after the clientFinished? + */ + if (!tls13_restore_handshake_digest_for_pha(s)) { + /* SSLfatal() already called */ + return 0; + } + st->hand_state = TLS_ST_CR_CERT_REQ; + return 1; + } + } break; } @@ -375,6 +395,13 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s) * ossl_statem_client_write_transition(). */ switch (st->hand_state) { + case TLS_ST_CR_CERT_REQ: + if (s->post_handshake_auth == SSL_PHA_REQUESTED) { + st->hand_state = TLS_ST_CW_CERT; + return WRITE_TRAN_CONTINUE; + } + /* Fall through */ + default: /* Shouldn't happen */ SSLfatal(s, SSL_AD_INTERNAL_ERROR, @@ -798,11 +825,17 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst) return WORK_MORE_B; if (SSL_IS_TLS13(s)) { - if (!s->method->ssl3_enc->change_cipher_state(s, - SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) { + if (!tls13_save_handshake_digest_for_pha(s)) { /* SSLfatal() already called */ return WORK_ERROR; } + if (s->post_handshake_auth != SSL_PHA_REQUESTED) { + if (!s->method->ssl3_enc->change_cipher_state(s, + SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) { + /* SSLfatal() already called */ + return WORK_ERROR; + } + } } break; @@ -2399,9 +2432,11 @@ MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s, PACKET *pkt) OPENSSL_free(s->s3->tmp.ctype); s->s3->tmp.ctype = NULL; s->s3->tmp.ctype_len = 0; + OPENSSL_free(s->pha_context); + s->pha_context = NULL; - /* TODO(TLS1.3) need to process request context, for now ignore */ - if (!PACKET_get_length_prefixed_1(pkt, &reqctx)) { + if (!PACKET_get_length_prefixed_1(pkt, &reqctx) || + !PACKET_memdup(&reqctx, &s->pha_context, &s->pha_context_len)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST, SSL_R_LENGTH_MISMATCH); @@ -3332,6 +3367,7 @@ static int ssl3_check_client_certificate(SSL *s) if (s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT && !tls1_check_chain(s, NULL, NULL, NULL, -2)) return 0; + return 1; } @@ -3357,8 +3393,12 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst) } s->rwstate = SSL_NOTHING; } - if (ssl3_check_client_certificate(s)) + if (ssl3_check_client_certificate(s)) { + if (s->post_handshake_auth == SSL_PHA_REQUESTED) { + return WORK_FINISHED_STOP; + } return WORK_FINISHED_CONTINUE; + } /* Fall through to WORK_MORE_B */ wst = WORK_MORE_B; @@ -3403,6 +3443,8 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst) } } + if (s->post_handshake_auth == SSL_PHA_REQUESTED) + return WORK_FINISHED_STOP; return WORK_FINISHED_CONTINUE; } @@ -3414,14 +3456,19 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst) int tls_construct_client_certificate(SSL *s, WPACKET *pkt) { - /* - * TODO(TLS1.3): For now we must put an empty context. Needs to be filled in - * later - */ - if (SSL_IS_TLS13(s) && !WPACKET_put_bytes_u8(pkt, 0)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR); - return 0; + if (SSL_IS_TLS13(s)) { + if (s->pha_context == NULL) { + /* no context available, add 0-length context */ + if (!WPACKET_put_bytes_u8(pkt, 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR); + return 0; + } + } else if (!WPACKET_sub_memcpy_u8(pkt, s->pha_context, s->pha_context_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR); + return 0; + } } if (!ssl3_output_cert_chain(s, pkt, (s->s3->tmp.cert_req == 2) ? NULL diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index 87ce280847..f57f33c4a5 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved * * Licensed under the OpenSSL license (the "License"). You may not use @@ -43,12 +43,15 @@ int ssl3_do_write(SSL *s, int type) /* * should not be done for 'Hello Request's, but in that case we'll * ignore the result anyway + * TLS1.3 KeyUpdate and NewSessionTicket do not need to be added */ - if (!ssl3_finish_mac(s, - (unsigned char *)&s->init_buf->data[s->init_off], - written)) - return -1; - + if (!SSL_IS_TLS13(s) || (s->statem.hand_state != TLS_ST_SW_SESSION_TICKET + && s->statem.hand_state != TLS_ST_CW_KEY_UPDATE + && s->statem.hand_state != TLS_ST_SW_KEY_UPDATE)) + if (!ssl3_finish_mac(s, + (unsigned char *)&s->init_buf->data[s->init_off], + written)) + return -1; if (written == s->init_num) { if (s->msg_callback) s->msg_callback(1, s->version, type, s->init_buf->data, @@ -504,7 +507,7 @@ int tls_construct_finished(SSL *s, WPACKET *pkt) size_t slen; /* This is a real handshake so make sure we clean it up at the end */ - if (!s->server) + if (!s->server && s->post_handshake_auth != SSL_PHA_REQUESTED) s->statem.cleanuphand = 1; /* @@ -741,8 +744,14 @@ MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt) /* This is a real handshake so make sure we clean it up at the end */ - if (s->server) - s->statem.cleanuphand = 1; + if (s->server) { + if (s->post_handshake_auth != SSL_PHA_REQUESTED) + s->statem.cleanuphand = 1; + if (SSL_IS_TLS13(s) && !tls13_save_handshake_digest_for_pha(s)) { + /* SSLfatal() already called */ + return MSG_PROCESS_ERROR; + } + } /* * In TLSv1.3 a Finished message signals a key change so the end of the @@ -801,7 +810,8 @@ MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt) */ if (SSL_IS_TLS13(s)) { if (s->server) { - if (!s->method->ssl3_enc->change_cipher_state(s, + if (s->post_handshake_auth != SSL_PHA_REQUESTED && + !s->method->ssl3_enc->change_cipher_state(s, SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)) { /* SSLfatal() already called */ return MSG_PROCESS_ERROR; @@ -1021,6 +1031,10 @@ WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst, int clearbufs, int stop) s->init_num = 0; } + if (SSL_IS_TLS13(s) && !s->server + && s->post_handshake_auth == SSL_PHA_REQUESTED) + s->post_handshake_auth = SSL_PHA_EXT_SENT; + if (s->statem.cleanuphand) { /* skipped if we just sent a HelloRequest */ s->renegotiate = 0; @@ -1237,18 +1251,24 @@ int tls_get_message_body(SSL *s, size_t *len) /* * We defer feeding in the HRR until later. We'll do it as part of * processing the message + * The TLsv1.3 handshake transcript stops at the ClientFinished + * message. */ #define SERVER_HELLO_RANDOM_OFFSET (SSL3_HM_HEADER_LENGTH + 2) - if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO - || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE - || memcmp(hrrrandom, - s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET, - SSL3_RANDOM_SIZE) != 0) { - if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, - s->init_num + SSL3_HM_HEADER_LENGTH)) { - /* SSLfatal() already called */ - *len = 0; - return 0; + /* KeyUpdate and NewSessionTicket do not need to be added */ + if (!SSL_IS_TLS13(s) || (s->s3->tmp.message_type != SSL3_MT_NEWSESSION_TICKET + && s->s3->tmp.message_type != SSL3_MT_KEY_UPDATE)) { + if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO + || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE + || memcmp(hrrrandom, + s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET, + SSL3_RANDOM_SIZE) != 0) { + if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, + s->init_num + SSL3_HM_HEADER_LENGTH)) { + /* SSLfatal() already called */ + *len = 0; + return 0; + } } } if (s->msg_callback) @@ -2208,3 +2228,54 @@ size_t construct_key_exchange_tbs(SSL *s, unsigned char **ptbs, *ptbs = tbs; return tbslen; } + +/* + * Saves the current handshake digest for Post-Handshake Auth, + * Done after ClientFinished is processed, done exactly once + */ +int tls13_save_handshake_digest_for_pha(SSL *s) +{ + if (s->pha_dgst == NULL) { + if (!ssl3_digest_cached_records(s, 1)) + /* SSLfatal() already called */ + return 0; + + s->pha_dgst = EVP_MD_CTX_new(); + if (s->pha_dgst == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA, + ERR_R_INTERNAL_ERROR); + return 0; + } + if (!EVP_MD_CTX_copy_ex(s->pha_dgst, + s->s3->handshake_dgst)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS13_SAVE_HANDSHAKE_DIGEST_FOR_PHA, + ERR_R_INTERNAL_ERROR); + return 0; + } + } + return 1; +} + +/* + * Restores the Post-Handshake Auth handshake digest + * Done just before sending/processing the Cert Request + */ +int tls13_restore_handshake_digest_for_pha(SSL *s) +{ + if (s->pha_dgst == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA, + ERR_R_INTERNAL_ERROR); + return 0; + } + if (!EVP_MD_CTX_copy_ex(s->s3->handshake_dgst, + s->pha_dgst)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS13_RESTORE_HANDSHAKE_DIGEST_FOR_PHA, + ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h index f16d3cba4e..af081ebccc 100644 --- a/ssl/statem/statem_locl.h +++ b/ssl/statem/statem_locl.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2017 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2015-2018 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -235,6 +235,8 @@ int tls_parse_ctos_psk_kex_modes(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +int tls_parse_ctos_post_handshake_auth(SSL *, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, @@ -365,6 +367,9 @@ EXT_RETURN tls_construct_ctos_padding(SSL *s, WPACKET *pkt, size_t chainidx); EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); + int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, unsigned int context, @@ -411,3 +416,6 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_handle_alpn(SSL *s); + +int tls13_save_handshake_digest_for_pha(SSL *s); +int tls13_restore_handshake_digest_for_pha(SSL *s); diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 51b6ce91bc..812780a416 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved * Copyright 2005 Nokia. All rights reserved. * @@ -107,6 +107,13 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt) */ if (s->early_data_state == SSL_EARLY_DATA_READING) break; + + if (mt == SSL3_MT_CERTIFICATE + && s->post_handshake_auth == SSL_PHA_REQUESTED) { + st->hand_state = TLS_ST_SR_CERT; + return 1; + } + if (mt == SSL3_MT_KEY_UPDATE) { st->hand_state = TLS_ST_SR_KEY_UPDATE; return 1; @@ -325,16 +332,22 @@ static int send_server_key_exchange(SSL *s) * 1: Yes * 0: No */ -static int send_certificate_request(SSL *s) +int send_certificate_request(SSL *s) { if ( /* don't request cert unless asked for it: */ s->verify_mode & SSL_VERIFY_PEER /* + * don't request if post-handshake-only unless doing + * post-handshake in TLSv1.3: + */ + && (!SSL_IS_TLS13(s) || !(s->verify_mode & SSL_VERIFY_POST_HANDSHAKE) + || s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) + /* * if SSL_VERIFY_CLIENT_ONCE is set, don't request cert - * during re-negotiation: + * a second time: */ - && (s->s3->tmp.finish_md_len == 0 || + && (s->certreqs_sent < 1 || !(s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) /* * never request cert in anonymous ciphersuites (see @@ -388,6 +401,10 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) st->hand_state = TLS_ST_SW_KEY_UPDATE; return WRITE_TRAN_CONTINUE; } + if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) { + st->hand_state = TLS_ST_SW_CERT_REQ; + return WRITE_TRAN_CONTINUE; + } /* Try to read from the client instead */ return WRITE_TRAN_FINISHED; @@ -423,7 +440,12 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) return WRITE_TRAN_CONTINUE; case TLS_ST_SW_CERT_REQ: - st->hand_state = TLS_ST_SW_CERT; + if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) { + s->post_handshake_auth = SSL_PHA_REQUESTED; + st->hand_state = TLS_ST_OK; + } else { + st->hand_state = TLS_ST_SW_CERT; + } return WRITE_TRAN_CONTINUE; case TLS_ST_SW_CERT: @@ -450,6 +472,8 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) * and give the application the opportunity to delay sending the * session ticket? */ + if (s->post_handshake_auth == SSL_PHA_REQUESTED) + s->post_handshake_auth = SSL_PHA_EXT_RECEIVED; st->hand_state = TLS_ST_SW_SESSION_TICKET; return WRITE_TRAN_CONTINUE; @@ -863,6 +887,13 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst) } break; + case TLS_ST_SW_CERT_REQ: + if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) { + if (statem_flush(s) != 1) + return WORK_MORE_A; + } + break; + case TLS_ST_SW_KEY_UPDATE: if (statem_flush(s) != 1) return WORK_MORE_A; @@ -2702,12 +2733,30 @@ int tls_construct_server_key_exchange(SSL *s, WPACKET *pkt) int tls_construct_certificate_request(SSL *s, WPACKET *pkt) { if (SSL_IS_TLS13(s)) { - /* TODO(TLS1.3) for now send empty request context */ - if (!WPACKET_put_bytes_u8(pkt, 0)) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, - SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST, - ERR_R_INTERNAL_ERROR); - return 0; + /* |