From d15c73859c93eb86809e6917235801835ebe46d6 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 29 Jul 2024 21:55:08 +0200 Subject: [PATCH] CONC-567 Schannel - handle SEC_I_RENEGOTIATE, prepare for TLSv1.3 There is no real renegotiation in TLSv1.3 protocol, so it is some internal schannel thing, that makes DecryptMessage() to return SEC_I_RENEGOTIATE, to replay a handshake step. This pops up when TLSv1.3 is enabled. --- libmariadb/secure/ma_schannel.c | 18 +++++++++--- libmariadb/secure/schannel.c | 49 +++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index be4148c7..af6ac479 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -100,6 +100,13 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRe return SEC_E_INSUFFICIENT_MEMORY; cbIoBuffer = 0; + + if (!InitialRead && pExtraData->cbBuffer) + { + memcpy(IoBuffer, pExtraData->pvBuffer,pExtraData->cbBuffer); + cbIoBuffer= pExtraData->cbBuffer; + } + fDoRead = InitialRead; /* handshake loop: We will leave if handshake is finished @@ -282,6 +289,8 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls) SecBufferDesc BufferOut; SecBuffer BuffersOut; + SecBufferDesc *pBuffersIn= NULL; + if (!ctls || !ctls->pvio) return 1; @@ -445,7 +454,7 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, } while (sRet == SEC_E_INCOMPLETE_MESSAGE); /* Continue reading until full message arrives */ - if (sRet != SEC_E_OK) + if (sRet != SEC_E_OK && sRet != SEC_I_RENEGOTIATE) { ma_schannel_set_sec_error(pvio, sRet); return sRet; @@ -462,7 +471,7 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, } - if (sctx->dataBuf.cbBuffer) + if (sctx->dataBuf.cbBuffer || sRet == SEC_I_RENEGOTIATE) { assert(sctx->dataBuf.pvBuffer); /* @@ -470,12 +479,13 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, Store the rest (if any) to be processed next time. */ nbytes = MIN(sctx->dataBuf.cbBuffer, ReadBufferSize); - memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes); + if (nbytes) + memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes); sctx->dataBuf.cbBuffer -= (unsigned long)nbytes; sctx->dataBuf.pvBuffer = (char *)sctx->dataBuf.pvBuffer + nbytes; *DecryptLength = (DWORD)nbytes; - return SEC_E_OK; + return sRet; } // No data buffer, loop } diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index c9d58a60..f3726102 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -472,13 +472,50 @@ ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length) SC_CTX *sctx= (SC_CTX *)ctls->ssl; MARIADB_PVIO *pvio= ctls->pvio; DWORD dlength= 0; - SECURITY_STATUS status = ma_schannel_read_decrypt(pvio, &sctx->hCtxt, &dlength, (uchar *)buffer, (DWORD)length); - if (status == SEC_I_CONTEXT_EXPIRED) - return 0; /* other side shut down the connection. */ - if (status == SEC_I_RENEGOTIATE) - return -1; /* Do not handle renegotiate yet */ + SECURITY_STATUS status; + SecBuffer tmp_extra_buf= {0}; - return (status == SEC_E_OK)? (ssize_t)dlength : -1; +retry: + status= ma_schannel_read_decrypt(pvio, &sctx->hCtxt, &dlength, + (uchar *) buffer, (DWORD) length); + if (tmp_extra_buf.cbBuffer) + { + /* + This memory was allocated in renegotiation processing + below, free it. + */ + LocalFree(tmp_extra_buf.pvBuffer); + tmp_extra_buf.cbBuffer= 0; + } + switch (status) { + case SEC_E_OK: + return (ssize_t) dlength; + case SEC_I_CONTEXT_EXPIRED: + /* Other side shut down the connection. */ + return 0; + case SEC_I_RENEGOTIATE: + /* Rerun handshake steps */ + tmp_extra_buf= sctx->extraBuf; + tmp_extra_buf.BufferType= SECBUFFER_TOKEN; + sctx->extraBuf.cbBuffer= 0; + sctx->extraBuf.pvBuffer= NULL; + status= ma_schannel_handshake_loop(pvio, FALSE, &tmp_extra_buf); + sctx->extraBuf= tmp_extra_buf; + if (status != SEC_E_OK) + return -1; + /* + If decrypt returned some decrypted bytes prior to + renegotiation, return them. + Otherwise, retry the read-decrypt again + */ + if (dlength) + return dlength; + + goto retry; + + default: + return -1; + } } ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)