summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2017-10-28 12:38:52 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2017-12-03 20:15:47 +0100
commit0a74f00f545a9993760e3533a5f0b768706fe84d (patch)
tree8d5d15d5389192ef8180d44349abd3f042daacbb
parentc2c9a21b6bcd091d2492829eeee6312431a0f2b8 (diff)
downloadgnutls-0a74f00f545a9993760e3533a5f0b768706fe84d.tar.gz
gnutls_*handshake: wrap gnutls_session_key_update under TLS 1.3
The semantics of the gnutls_handshake() and gnutls_rehandshake() functions were tied to TLS 1.2 and earlier behavior. This patch attempts to merge the two different semantics as follows: TLS1.2: * gnutls_rehandshake: sends a hello request message (asks the peer for a re-handshake) in server side; invalid to be called in client side. * gnutls_handshake: performs a re-handshake in either client or server side; in server side it is expected to be called after gnutls_rehandshake(). TLS1.3: * gnutls_rehandshake: in server side sends a key update and asks the peer to re-key as well; remains invalid to be called in client side. * gnutls_handshake: sends a key update and asks the peer to re-key as well; in client side; is a no-op when called in server side. Relates #131 Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
-rw-r--r--lib/handshake.c109
1 files changed, 68 insertions, 41 deletions
diff --git a/lib/handshake.c b/lib/handshake.c
index 8470c439b5..afd2d97045 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -2183,39 +2183,53 @@ recv_hello_verify_request(gnutls_session_t session,
* gnutls_rehandshake:
* @session: is a #gnutls_session_t type.
*
- * This function will renegotiate security parameters with the
- * client. This should only be called in case of a server.
+ * This function can only be called in server side, and
+ * instructs a TLS 1.2 or earlier client to renegotiate
+ * parameters (perform a handshake), by sending a
+ * hello request message.
*
- * This message informs the peer that we want to renegotiate
- * parameters (perform a handshake).
+ * If this function succeeds, the calling application
+ * should call gnutls_record_recv() until %GNUTLS_E_REHANDSHAKE
+ * is returned to clear any pending data. If the %GNUTLS_E_REHANDSHAKE
+ * error code is not seen, then the handshake request was
+ * not followed by the peer (the TLS protocol does not require
+ * the client to do, and such compliance should be handled
+ * by the application protocol).
*
- * If this function succeeds (returns 0), you must call the
- * gnutls_handshake() function in order to negotiate the new
+ * Once the %GNUTLS_E_REHANDSHAKE error code is seen, the
+ * calling application should proceed to calling
+ * gnutls_handshake() to negotiate the new
* parameters.
*
- * Since TLS is full duplex some application data might have been
- * sent during peer's processing of this message. In that case
- * one should call gnutls_record_recv() until GNUTLS_E_REHANDSHAKE
- * is returned to clear any pending data. Care must be taken, if
- * rehandshake is mandatory, to terminate if it does not start after
- * some threshold.
- *
* If the client does not wish to renegotiate parameters he
- * should reply with an alert message, thus the return code will be
- * %GNUTLS_E_WARNING_ALERT_RECEIVED and the alert will be
+ * may reply with an alert message, and in that case the return code seen
+ * by subsequent gnutls_record_recv() will be
+ * %GNUTLS_E_WARNING_ALERT_RECEIVED with the specific alert being
* %GNUTLS_A_NO_RENEGOTIATION. A client may also choose to ignore
- * this message.
+ * this request.
+ *
+ * Under TLS 1.3 this function is equivalent to gnutls_session_key_update()
+ * with the %GNUTLS_KU_PEER flag. In that case subsequent calls to
+ * gnutls_record_recv() will not return %GNUTLS_E_REHANDSHAKE, and
+ * calls to gnutls_handshake() in server side are a no-op.
+ *
+ * This function always fails with %GNUTLS_E_INVALID_REQUEST when
+ * called in client side.
*
* Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
**/
int gnutls_rehandshake(gnutls_session_t session)
{
int ret;
+ const version_entry_st *vers = get_version(session);
/* only server sends that handshake packet */
if (session->security_parameters.entity == GNUTLS_CLIENT)
return GNUTLS_E_INVALID_REQUEST;
+ if (vers->tls13_sem)
+ return gnutls_session_key_update(session, GNUTLS_KU_PEER);
+
_dtls_async_timer_delete(session);
ret =
@@ -2305,45 +2319,58 @@ static int _gnutls_recv_supplemental(gnutls_session_t session)
* gnutls_handshake:
* @session: is a #gnutls_session_t type.
*
- * This function does the handshake of the TLS/SSL protocol, and
- * initializes the TLS connection.
- *
- * This function will fail if any problem is encountered, and will
- * return a negative error code. In case of a client, if the client
- * has asked to resume a session, but the server couldn't, then a
- * full handshake will be performed.
+ * This function performs the handshake of the TLS/SSL protocol, and
+ * initializes the TLS session parameters.
*
* The non-fatal errors expected by this function are:
* %GNUTLS_E_INTERRUPTED, %GNUTLS_E_AGAIN,
- * %GNUTLS_E_WARNING_ALERT_RECEIVED, and %GNUTLS_E_GOT_APPLICATION_DATA,
- * the latter only in a case of rehandshake.
+ * %GNUTLS_E_WARNING_ALERT_RECEIVED. When this function is called
+ * for re-handshake under TLS 1.2 or earlier, the non-fatal error code
+ * %GNUTLS_E_GOT_APPLICATION_DATA may also be returned.
*
- * The former two interrupt the handshake procedure due to the lower
- * layer being interrupted, and the latter because of an alert that
- * may be sent by a server (it is always a good idea to check any
- * received alerts). On these errors call this function again, until it
- * returns 0; cf. gnutls_record_get_direction() and
+ * The former two interrupt the handshake procedure due to the transport
+ * layer being interrupted, and the latter because of a "warning" alert that
+ * was sent by the peer (it is always a good idea to check any
+ * received alerts). On these non-fatal errors call this function again,
+ * until it returns 0; cf. gnutls_record_get_direction() and
* gnutls_error_is_fatal(). In DTLS sessions the non-fatal error
* %GNUTLS_E_LARGE_PACKET is also possible, and indicates that
* the MTU should be adjusted.
*
- * If this function is called by a server after a rehandshake request
- * then %GNUTLS_E_GOT_APPLICATION_DATA or
- * %GNUTLS_E_WARNING_ALERT_RECEIVED may be returned. Note that these
- * are non fatal errors, only in the specific case of a rehandshake.
- * Their meaning is that the client rejected the rehandshake request or
- * in the case of %GNUTLS_E_GOT_APPLICATION_DATA it could also mean that
- * some data were pending. A client may receive that error code if
- * it initiates the handshake and the server doesn't agreed.
+ * When this function is called by a server after a rehandshake request
+ * under TLS 1.2 or earlier the %GNUTLS_E_GOT_APPLICATION_DATA error code indicates
+ * that some data were pending prior to peer initiating the handshake.
+ * Under TLS 1.3 this function when called after a successful handshake, is a no-op
+ * and always succeeds in server side; in client side this function is
+ * equivalent to gnutls_session_key_update() with %GNUTLS_KU_PEER flag.
*
- * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ * This function handles both full and abbreviated TLS handshakes (resumption).
+ * For abbreviated handshakes, in client side, the gnutls_session_set_data()
+ * should be called prior to this function to set parameters from a previous session.
+ * In server side, resumption is handled by either setting a DB back-end, or setting
+ * up keys for session tickets.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on a successful handshake, otherwise a negative error code.
**/
int gnutls_handshake(gnutls_session_t session)
{
int ret;
- /* sanity check. Verify that there are priorities setup.
- */
+ if (unlikely(session->internals.initial_negotiation_completed)) {
+ const version_entry_st *vers = get_version(session);
+
+ if (vers->tls13_sem) {
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ return gnutls_session_key_update(session, GNUTLS_KU_PEER);
+ } else {
+ /* This is a no-op for a server under TLS 1.3, as
+ * a server has already called gnutls_rehandshake()
+ * which performed a key update.
+ */
+ return 0;
+ }
+ }
+ }
if (STATE == STATE0) {
/* first call */