From 968416ce9513ad0cf484d7768748fd671b985237 Mon Sep 17 00:00:00 2001 From: David Fort Date: Wed, 19 Jan 2022 11:58:00 +0100 Subject: [PATCH] kerberos: fix encode/decode functions --- winpr/libwinpr/sspi/Kerberos/kerberos.c | 96 +++++++++++++++++++++---- 1 file changed, 81 insertions(+), 15 deletions(-) diff --git a/winpr/libwinpr/sspi/Kerberos/kerberos.c b/winpr/libwinpr/sspi/Kerberos/kerberos.c index bdc920df2..f3a48a2e7 100644 --- a/winpr/libwinpr/sspi/Kerberos/kerberos.c +++ b/winpr/libwinpr/sspi/Kerberos/kerberos.c @@ -17,7 +17,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -28,6 +27,7 @@ #include #include +#include #include #include #include @@ -57,6 +57,7 @@ struct _KRB_CONTEXT sspi_gss_cred_id_t cred; sspi_gss_ctx_id_t gss_ctx; sspi_gss_name_t target_name; + UINT32 trailerSize; }; static const char* KRB_PACKAGE_NAME = "Kerberos"; @@ -230,8 +231,7 @@ static int kerberos_SetContextServicePrincipalNameA(KRB_CONTEXT* context, } #ifdef WITH_GSSAPI -static krb5_error_code KRB5_CALLCONV acquire_cred(krb5_context ctx, krb5_principal client, - const char* password) +static krb5_error_code acquire_cred(krb5_context ctx, krb5_principal client, const char* password) { krb5_error_code ret; krb5_creds creds; @@ -614,6 +614,9 @@ static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(PCtxtHandle ph if (ulAttribute == SECPKG_ATTR_SIZES) { + UINT32 major_status, minor_status, max_unwrapped; + KRB_CONTEXT* context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer; /* The MaxTokenSize by default is 12,000 bytes. This has been the default value * since Windows 2000 SP2 and still remains in Windows 7 and Windows 2008 R2. @@ -622,8 +625,18 @@ static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(PCtxtHandle ph ContextSizes->cbMaxToken = KERBEROS_SecPkgInfoA.cbMaxToken; ContextSizes->cbMaxSignature = 0; /* means verify not supported */ ContextSizes->cbBlockSize = 0; /* padding not used */ - ContextSizes->cbSecurityTrailer = - 60; /* gss_wrap adds additional 60 bytes for encrypt message */ + + WINPR_ASSERT(context); + + major_status = sspi_gss_wrap_size_limit(&minor_status, context->gss_ctx, TRUE, + SSPI_GSS_C_QOP_DEFAULT, 12000, &max_unwrapped); + if (SSPI_GSS_ERROR(major_status)) + { + WLog_ERR(TAG, "unable to compute the trailer size with gss_wrap_size_limit()"); + return SEC_E_INTERNAL_ERROR; + } + + context->trailerSize = ContextSizes->cbSecurityTrailer = (12000 - max_unwrapped); return SEC_E_OK; } @@ -642,9 +655,13 @@ static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext, KRB_CONTEXT* context; sspi_gss_buffer_desc input; sspi_gss_buffer_desc output; + BYTE* ptr; + size_t toCopy; PSecBuffer data_buffer = NULL; - context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); + PSecBuffer sig_buffer = NULL; + SECURITY_STATUS ret = SEC_E_OK; + context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); if (!context) return SEC_E_INVALID_HANDLE; @@ -652,10 +669,23 @@ static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext, { if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA) data_buffer = &pMessage->pBuffers[index]; + else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN) + sig_buffer = &pMessage->pBuffers[index]; } - if (!data_buffer) + if (!data_buffer || !sig_buffer) + { + WLog_ERR(TAG, "kerberos_EncryptMessage: our winPR emulation can only handle calls with a " + "token AND a data buffer"); return SEC_E_INVALID_TOKEN; + } + + if ((BYTE*)sig_buffer->pvBuffer + sig_buffer->cbBuffer != data_buffer->pvBuffer) + { + WLog_ERR(TAG, "kerberos_EncryptMessage: our winPR emulation expects token and data buffer " + "to be contiguous"); + return SEC_E_INVALID_TOKEN; + } input.value = data_buffer->pvBuffer; input.length = data_buffer->cbBuffer; @@ -668,13 +698,36 @@ static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext, if (conf_state == 0) { WLog_ERR(TAG, "error: gss_wrap confidentiality was not applied"); - sspi_gss_release_buffer(&minor_status, &output); - return SEC_E_INTERNAL_ERROR; + ret = SEC_E_INTERNAL_ERROR; + goto out; } - CopyMemory(data_buffer->pvBuffer, output.value, output.length); + if (output.length < context->trailerSize) + { + WLog_ERR(TAG, "error: output is smaller than expected trailerSize"); + ret = SEC_E_INTERNAL_ERROR; + goto out; + } + + /* + * we artificially fill the sig buffer and data_buffer, even if gss_wrap() puts the + * mic and message in the same place + */ + ptr = output.value; + CopyMemory(sig_buffer->pvBuffer, ptr, context->trailerSize); + ptr += context->trailerSize; + + toCopy = output.length - context->trailerSize; + if (data_buffer->cbBuffer < toCopy) + { + ret = SEC_E_INTERNAL_ERROR; + goto out; + } + CopyMemory(data_buffer->pvBuffer, ptr, toCopy); + +out: sspi_gss_release_buffer(&minor_status, &output); - return SEC_E_OK; + return ret; } static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext, @@ -688,7 +741,7 @@ static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext, KRB_CONTEXT* context; sspi_gss_buffer_desc input_data; sspi_gss_buffer_desc output; - PSecBuffer data_buffer_to_unwrap = NULL; + PSecBuffer data_buffer_to_unwrap = NULL, sig_buffer = NULL; context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext); if (!context) @@ -698,14 +751,27 @@ static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext, { if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA) data_buffer_to_unwrap = &pMessage->pBuffers[index]; + else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN) + sig_buffer = &pMessage->pBuffers[index]; } - if (!data_buffer_to_unwrap) + if (!data_buffer_to_unwrap || !sig_buffer) + { + WLog_ERR(TAG, "kerberos_DecryptMessage: our winPR emulation can only handle calls with a " + "token AND a data buffer"); return SEC_E_INVALID_TOKEN; + } + + if ((BYTE*)sig_buffer->pvBuffer + sig_buffer->cbBuffer != data_buffer_to_unwrap->pvBuffer) + { + WLog_ERR(TAG, "kerberos_DecryptMessage: our winPR emulation expects token and data buffer " + "to be contiguous"); + return SEC_E_INVALID_TOKEN; + } /* unwrap encrypted TLS key AND its signature */ - input_data.value = data_buffer_to_unwrap->pvBuffer; - input_data.length = data_buffer_to_unwrap->cbBuffer; + input_data.value = sig_buffer->pvBuffer; + input_data.length = sig_buffer->cbBuffer + data_buffer_to_unwrap->cbBuffer; major_status = sspi_gss_unwrap(&minor_status, context->gss_ctx, &input_data, &output, &conf_state, NULL);