From: Piotr Caban <piotr@codeweavers.com> --- dlls/kerberos/krb5_ap.c | 331 ++++++++++++++++++++++++++++------------ dlls/kerberos/unixlib.c | 97 ++++++++++++ dlls/kerberos/unixlib.h | 16 ++ 3 files changed, 349 insertions(+), 95 deletions(-) diff --git a/dlls/kerberos/krb5_ap.c b/dlls/kerberos/krb5_ap.c index 074a99eddfb..b2e81554428 100644 --- a/dlls/kerberos/krb5_ap.c +++ b/dlls/kerberos/krb5_ap.c @@ -33,6 +33,7 @@ #include "ntsecpkg.h" #include "winternl.h" +#include "wine/list.h" #include "wine/debug.h" #include "unixlib.h" @@ -81,6 +82,24 @@ struct context_handle UINT64 handle; }; +struct user_ctx +{ + struct list entry; + LSA_SEC_HANDLE handle; + + UINT64 context; +}; + +static struct list user_ctx_list = LIST_INIT(user_ctx_list); +static CRITICAL_SECTION user_ctx_cs; +static CRITICAL_SECTION_DEBUG user_ctx_debug = +{ + 0, 0, &user_ctx_cs, + { &user_ctx_debug.ProcessLocksList, &user_ctx_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": user_ctx_cs") } +}; +static CRITICAL_SECTION user_ctx_cs = { &user_ctx_debug, -1, 0, 0, 0, 0 }; + static LSA_SEC_HANDLE create_context_handle( struct context_handle *ctx, UINT64 new_context ) { UINT64 context = ctx ? ctx->handle : 0; @@ -664,25 +683,53 @@ static NTSTATUS NTAPI kerberos_SpInitLsaModeContext( LSA_SEC_HANDLE credential, params.output_token_length = &output->pBuffers[idx].cbBuffer; status = KRB5_CALL( initialize_context, ¶ms ); - if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) + if (status == SEC_E_OK) { - *new_context = create_context_handle( context_handle, new_context_handle ); - if (context_attr && (context_req & ISC_REQ_ALLOCATE_MEMORY)) - *context_attr |= ISC_RET_ALLOCATED_MEMORY; + struct export_context_params params; + ULONG size = 4096; + + params.context = &new_context_handle; + while (1) + { + context_data->pvBuffer = lsa_funcs->AllocateLsaHeap( size ); + if (!context_data->pvBuffer) + { + status = STATUS_NO_MEMORY; + } + else + { + params.buf = context_data->pvBuffer; + params.size = &size; + status = KRB5_CALL( export_context, ¶ms ); + } + if (!status) break; + + lsa_funcs->FreeLsaHeap( context_data->pvBuffer ); + context_data->pvBuffer = NULL; + if (status != STATUS_BUFFER_TOO_SMALL) break; + } - if (status == SEC_E_OK) + if (!status) { - /* FIXME: *mapped_context = TRUE; */ + context_data->cbBuffer = size; + + *mapped_context = TRUE; expiry_to_timestamp( exptime, expiry ); } } + + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) + { + *new_context = create_context_handle( context_handle, new_context_handle ); + if (context_attr && (context_req & ISC_REQ_ALLOCATE_MEMORY)) + *context_attr |= ISC_RET_ALLOCATED_MEMORY; + } else { if (context_req & ISC_REQ_ALLOCATE_MEMORY) RtlFreeHeap( GetProcessHeap(), 0, output->pBuffers[idx].pvBuffer ); } } - /* FIXME: initialize context_data */ free( target ); return status; } @@ -728,14 +775,43 @@ static NTSTATUS NTAPI kerberos_SpAcceptLsaModeContext( LSA_SEC_HANDLE credential /* FIXME: check if larger output buffer exists */ status = KRB5_CALL( accept_context, ¶ms ); - if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) - *new_context = create_context_handle( context_handle, new_context_handle ); if (!status) { - /* FIXME: *mapped_context = TRUE; */ - expiry_to_timestamp( exptime, expiry ); + struct export_context_params params; + ULONG size = 4096; + + params.context = &new_context_handle; + while (1) + { + context_data->pvBuffer = lsa_funcs->AllocateLsaHeap( size ); + if (!context_data->pvBuffer) + { + status = STATUS_NO_MEMORY; + } + else + { + params.buf = context_data->pvBuffer; + params.size = &size; + status = KRB5_CALL( export_context, ¶ms ); + } + if (!status) break; + + lsa_funcs->FreeLsaHeap( context_data->pvBuffer ); + context_data->pvBuffer = NULL; + if (status != STATUS_BUFFER_TOO_SMALL) break; + } + + if (!status) + { + context_data->cbBuffer = size; + + *mapped_context = TRUE; + expiry_to_timestamp( exptime, expiry ); + } } - /* FIXME: initialize context_data */ + + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) + *new_context = create_context_handle( context_handle, new_context_handle ); } return status; } @@ -978,134 +1054,199 @@ static NTSTATUS NTAPI kerberos_SpInstanceInit(ULONG version, SECPKG_DLL_FUNCTION return STATUS_SUCCESS; } +static struct user_ctx* find_user_ctx( LSA_SEC_HANDLE handle ) +{ + struct user_ctx *ret; + + EnterCriticalSection( &user_ctx_cs ); + LIST_FOR_EACH_ENTRY( ret, &user_ctx_list, struct user_ctx, entry ) + { + if (ret->handle == handle) + { + LeaveCriticalSection( &user_ctx_cs ); + return ret; + } + } + LeaveCriticalSection( &user_ctx_cs ); + return NULL; +} + +static NTSTATUS NTAPI kerberos_SpInitUserModeContext( LSA_SEC_HANDLE handle, SecBuffer *buf ) +{ + struct import_context_params params; + struct user_ctx *ctx; + UINT64 context; + NTSTATUS status; + + TRACE( "%Ix, %p\n", handle, buf ); + + params.buf = buf->pvBuffer; + params.size = buf->cbBuffer; + params.context = &context; + status = KRB5_CALL( import_context, ¶ms ); + FIXME("importing context: %lx\n", status); + if (status) return status; + + EnterCriticalSection( &user_ctx_cs ); + ctx = find_user_ctx( handle ); + if (!ctx) + { + ctx = malloc( sizeof(*ctx) ); + if (!ctx) + { + struct delete_context_params del_params; + + del_params.context = context; + KRB5_CALL( delete_context, &del_params ); + LeaveCriticalSection( &user_ctx_cs ); + return SEC_E_INSUFFICIENT_MEMORY; + } + list_add_head( &user_ctx_list, &ctx->entry ); + } + + ctx->handle = handle; + ctx->context = context; + LeaveCriticalSection( &user_ctx_cs ); + return SEC_E_OK; +} + static NTSTATUS SEC_ENTRY kerberos_SpMakeSignature( LSA_SEC_HANDLE context, ULONG quality_of_protection, SecBufferDesc *message, ULONG message_seq_no ) { + struct make_signature_params params; + int data_idx, token_idx; + struct user_ctx *ctx; + TRACE( "%Ix, %#lx, %p, %lu\n", context, quality_of_protection, message, message_seq_no ); if (quality_of_protection) FIXME( "ignoring quality_of_protection %#lx\n", quality_of_protection ); if (message_seq_no) FIXME( "ignoring message_seq_no %lu\n", message_seq_no ); - if (context) - { - struct context_handle *context_handle = (void *)context; - struct make_signature_params params; - int data_idx, token_idx; - - /* FIXME: multiple data buffers, read-only buffers */ - if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; - if ((token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; - - params.context = context_handle->handle; - params.data_length = message->pBuffers[data_idx].cbBuffer; - params.data = message->pBuffers[data_idx].pvBuffer; - params.token_length = &message->pBuffers[token_idx].cbBuffer; - params.token = message->pBuffers[token_idx].pvBuffer; + if (!(ctx = find_user_ctx( context ))) return SEC_E_INVALID_HANDLE; + /* FIXME: multiple data buffers, read-only buffers */ + if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; + if ((token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; - return KRB5_CALL( make_signature, ¶ms ); - } - else return SEC_E_INVALID_HANDLE; + params.context = ctx->context; + params.data_length = message->pBuffers[data_idx].cbBuffer; + params.data = message->pBuffers[data_idx].pvBuffer; + params.token_length = &message->pBuffers[token_idx].cbBuffer; + params.token = message->pBuffers[token_idx].pvBuffer; + + return KRB5_CALL( make_signature, ¶ms ); } static NTSTATUS NTAPI kerberos_SpVerifySignature( LSA_SEC_HANDLE context, SecBufferDesc *message, ULONG message_seq_no, ULONG *quality_of_protection ) { + struct verify_signature_params params; + int data_idx, token_idx; + struct user_ctx *ctx; + TRACE( "%Ix, %p, %lu, %p\n", context, message, message_seq_no, quality_of_protection ); if (message_seq_no) FIXME( "ignoring message_seq_no %lu\n", message_seq_no ); - if (context) - { - struct context_handle *context_handle = (void *)context; - struct verify_signature_params params; - int data_idx, token_idx; + if (!(ctx = find_user_ctx( context ))) return SEC_E_INVALID_HANDLE; + if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; + if ((token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; - if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; - if ((token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; + params.context = ctx->context; + params.data_length = message->pBuffers[data_idx].cbBuffer; + params.data = message->pBuffers[data_idx].pvBuffer; + params.token_length = message->pBuffers[token_idx].cbBuffer; + params.token = message->pBuffers[token_idx].pvBuffer; + params.qop = quality_of_protection; - params.context = context_handle->handle; - params.data_length = message->pBuffers[data_idx].cbBuffer; - params.data = message->pBuffers[data_idx].pvBuffer; - params.token_length = message->pBuffers[token_idx].cbBuffer; - params.token = message->pBuffers[token_idx].pvBuffer; - params.qop = quality_of_protection; - - return KRB5_CALL( verify_signature, ¶ms ); - } - else return SEC_E_INVALID_HANDLE; + return KRB5_CALL( verify_signature, ¶ms ); } static NTSTATUS NTAPI kerberos_SpSealMessage( LSA_SEC_HANDLE context, ULONG quality_of_protection, SecBufferDesc *message, ULONG message_seq_no ) { + struct seal_message_params params; + int data_idx, token_idx; + struct user_ctx *ctx; + TRACE( "%Ix, %#lx, %p, %lu\n", context, quality_of_protection, message, message_seq_no ); if (message_seq_no) FIXME( "ignoring message_seq_no %lu\n", message_seq_no ); - if (context) - { - struct context_handle *context_handle = (void *)context; - struct seal_message_params params; - int data_idx, token_idx; - - /* FIXME: multiple data buffers, read-only buffers */ - if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; - if ((token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; - - params.context = context_handle->handle; - params.data_length = message->pBuffers[data_idx].cbBuffer; - params.data = message->pBuffers[data_idx].pvBuffer; - params.token_length = &message->pBuffers[token_idx].cbBuffer; - params.token = message->pBuffers[token_idx].pvBuffer; - params.qop = quality_of_protection; + if (!(ctx = find_user_ctx( context ))) return SEC_E_INVALID_HANDLE; + /* FIXME: multiple data buffers, read-only buffers */ + if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; + if ((token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; - return KRB5_CALL( seal_message, ¶ms ); - } - else return SEC_E_INVALID_HANDLE; + params.context = ctx->context; + params.data_length = message->pBuffers[data_idx].cbBuffer; + params.data = message->pBuffers[data_idx].pvBuffer; + params.token_length = &message->pBuffers[token_idx].cbBuffer; + params.token = message->pBuffers[token_idx].pvBuffer; + params.qop = quality_of_protection; + + return KRB5_CALL( seal_message, ¶ms ); } static NTSTATUS NTAPI kerberos_SpUnsealMessage( LSA_SEC_HANDLE context, SecBufferDesc *message, ULONG message_seq_no, ULONG *quality_of_protection ) { + struct unseal_message_params params; + int stream_idx, data_idx, token_idx = -1; + struct user_ctx *ctx; + TRACE( "%Ix, %p, %lu, %p\n", context, message, message_seq_no, quality_of_protection ); if (message_seq_no) FIXME( "ignoring message_seq_no %lu\n", message_seq_no ); - if (context) + if (!(ctx = find_user_ctx( context ))) return SEC_E_INVALID_HANDLE; + if ((stream_idx = get_buffer_index( message, SECBUFFER_STREAM )) == -1 && + (token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; + if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; + + params.context = ctx->context; + + if (token_idx != -1) { - struct context_handle *context_handle = (void *)context; - struct unseal_message_params params; - int stream_idx, data_idx, token_idx = -1; + params.stream_length = 0; + params.stream = NULL; + params.token_length = message->pBuffers[token_idx].cbBuffer; + params.token = message->pBuffers[token_idx].pvBuffer; + } + else + { + params.stream_length = message->pBuffers[stream_idx].cbBuffer; + params.stream = message->pBuffers[stream_idx].pvBuffer; + params.token_length = 0; + params.token = NULL; + } + params.data_length = &message->pBuffers[data_idx].cbBuffer; + params.data = (BYTE **)&message->pBuffers[data_idx].pvBuffer; + params.qop = quality_of_protection; - if ((stream_idx = get_buffer_index( message, SECBUFFER_STREAM )) == -1 && - (token_idx = get_buffer_index( message, SECBUFFER_TOKEN )) == -1) return SEC_E_INVALID_TOKEN; - if ((data_idx = get_buffer_index( message, SECBUFFER_DATA )) == -1) return SEC_E_INVALID_TOKEN; + return KRB5_CALL( unseal_message, ¶ms ); +} - params.context = context_handle->handle; +static NTSTATUS NTAPI kerberos_SpDeleteUserModeContext( LSA_SEC_HANDLE handle ) +{ + struct delete_context_params params; + struct user_ctx *user_ctx; - if (token_idx != -1) - { - params.stream_length = 0; - params.stream = NULL; - params.token_length = message->pBuffers[token_idx].cbBuffer; - params.token = message->pBuffers[token_idx].pvBuffer; - } - else - { - params.stream_length = message->pBuffers[stream_idx].cbBuffer; - params.stream = message->pBuffers[stream_idx].pvBuffer; - params.token_length = 0; - params.token = NULL; - } - params.data_length = &message->pBuffers[data_idx].cbBuffer; - params.data = (BYTE **)&message->pBuffers[data_idx].pvBuffer; - params.qop = quality_of_protection; + TRACE( "%Ix\n", handle ); + + EnterCriticalSection( &user_ctx_cs ); + user_ctx = find_user_ctx( handle ); + if (user_ctx) list_remove( &user_ctx->entry ); + LeaveCriticalSection( &user_ctx_cs ); - return KRB5_CALL( unseal_message, ¶ms ); + if (user_ctx) + { + params.context = user_ctx->context; + KRB5_CALL( delete_context, ¶ms ); + free( user_ctx ); } - else return SEC_E_INVALID_HANDLE; + return STATUS_SUCCESS; } static SECPKG_USER_FUNCTION_TABLE kerberos_user_table = { kerberos_SpInstanceInit, - NULL, /* SpInitUserModeContext */ + kerberos_SpInitUserModeContext, kerberos_SpMakeSignature, kerberos_SpVerifySignature, kerberos_SpSealMessage, @@ -1113,7 +1254,7 @@ static SECPKG_USER_FUNCTION_TABLE kerberos_user_table = NULL, /* SpGetContextToken */ NULL, /* SpQueryContextAttributes */ NULL, /* SpCompleteAuthToken */ - NULL, /* SpDeleteContext */ + kerberos_SpDeleteUserModeContext, NULL, /* SpFormatCredentialsFn */ NULL, /* SpMarshallSupplementalCreds */ NULL, /* SpExportSecurityContext */ diff --git a/dlls/kerberos/unixlib.c b/dlls/kerberos/unixlib.c index 70ba78897d5..ede1cd06fdb 100644 --- a/dlls/kerberos/unixlib.c +++ b/dlls/kerberos/unixlib.c @@ -398,8 +398,10 @@ MAKE_FUNCPTR( gss_accept_sec_context ); MAKE_FUNCPTR( gss_acquire_cred ); MAKE_FUNCPTR( gss_delete_sec_context ); MAKE_FUNCPTR( gss_display_status ); +MAKE_FUNCPTR( gss_export_sec_context ); MAKE_FUNCPTR( gss_get_mic ); MAKE_FUNCPTR( gss_import_name ); +MAKE_FUNCPTR( gss_import_sec_context ); MAKE_FUNCPTR( gss_init_sec_context ); MAKE_FUNCPTR( gss_inquire_context ); MAKE_FUNCPTR( gss_inquire_sec_context_by_oid ); @@ -434,8 +436,10 @@ static BOOL load_gssapi_krb5(void) LOAD_FUNCPTR( gss_acquire_cred ) LOAD_FUNCPTR( gss_delete_sec_context ) LOAD_FUNCPTR( gss_display_status ) + LOAD_FUNCPTR( gss_export_sec_context ); LOAD_FUNCPTR( gss_get_mic ) LOAD_FUNCPTR( gss_import_name ) + LOAD_FUNCPTR( gss_import_sec_context ); LOAD_FUNCPTR( gss_init_sec_context ) LOAD_FUNCPTR( gss_inquire_context ) LOAD_FUNCPTR( gss_inquire_sec_context_by_oid ) @@ -698,6 +702,44 @@ static NTSTATUS delete_context( void *args ) return status_gss_to_sspi( ret ); } +static NTSTATUS export_context( void *args ) +{ + struct export_context_params *params = args; + gss_ctx_id_t ctx_handle = ctxhandle_sspi_to_gss( *params->context ); + gss_buffer_desc data = GSS_C_EMPTY_BUFFER; + OM_uint32 ret, minor_status; + NTSTATUS status = STATUS_SUCCESS; + + ret = pgss_export_sec_context( &minor_status, &ctx_handle, &data ); + TRACE( "gss_export_sec_context returned %#x minor status %#x\n", ret, minor_status ); + if (GSS_ERROR( ret )) + { + trace_gss_status( ret, minor_status ); + return status_gss_to_sspi( ret ); + } + + /* FIXME: don't re-import the context on Lsa side */ + ret = pgss_import_sec_context( &minor_status, &data, &ctx_handle ); + TRACE( "gss_import_sec_context returned %#x minor status %#x\n", ret, minor_status ); + if (GSS_ERROR( ret )) + { + pgss_release_buffer(&minor_status, &data); + *params->context = 0; + trace_gss_status( ret, minor_status ); + return status_gss_to_sspi( ret ); + } + + TRACE( "exported context size: %d\n", (int)data.length ); + if (*params->size < data.length) + status = STATUS_BUFFER_TOO_SMALL; + else + memcpy( params->buf, data.value, data.length ); + ctxhandle_gss_to_sspi( ctx_handle, params->context ); + *params->size = data.length; + pgss_release_buffer(&minor_status, &data); + return status; +} + static NTSTATUS free_credentials_handle( void *args ) { const struct free_credentials_handle_params *params = args; @@ -710,6 +752,23 @@ static NTSTATUS free_credentials_handle( void *args ) return status_gss_to_sspi( ret ); } +static NTSTATUS import_context( void *args ) +{ + struct import_context_params *params = args; + gss_ctx_id_t ctx_handle = GSS_C_NO_CONTEXT; + OM_uint32 ret, minor_status; + gss_buffer_desc data; + + data.length = params->size; + data.value = params->buf; + ret = pgss_import_sec_context( &minor_status, &data, &ctx_handle ); + TRACE( "gss_import_sec_context returned %#x minor status %#x\n", ret, minor_status ); + if (GSS_ERROR( ret )) trace_gss_status( ret, minor_status ); + + ctxhandle_gss_to_sspi( ctx_handle, params->context ); + return status_gss_to_sspi( ret ); +} + static ULONG flags_isc_req_to_gss( ULONG flags ) { ULONG ret = 0; @@ -1127,8 +1186,10 @@ const unixlib_entry_t __wine_unix_call_funcs[] = accept_context, acquire_credentials_handle, delete_context, + export_context, free_credentials_handle, initialize_context, + import_context, make_signature, query_context_attributes, query_ticket_cache, @@ -1208,6 +1269,23 @@ static NTSTATUS wow64_delete_context( void *args ) return delete_context( ¶ms ); } +static NTSTATUS wow64_export_context( void *args ) +{ + struct + { + PTR32 context; + PTR32 buf; + PTR32 size; + } const *params32 = args; + struct export_context_params params = + { + ULongToPtr(params32->context), + ULongToPtr(params32->buf), + ULongToPtr(params32->size), + }; + return export_context( ¶ms ); +} + static NTSTATUS wow64_free_credentials_handle( void *args ) { struct @@ -1254,6 +1332,23 @@ static NTSTATUS wow64_initialize_context( void *args ) return initialize_context( ¶ms ); } +static NTSTATUS wow64_import_context( void *args ) +{ + struct + { + PTR32 buf; + ULONG size; + PTR32 context; + } const *params32 = args; + struct import_context_params params = + { + ULongToPtr(params32->buf), + params32->size, + ULongToPtr(params32->context), + }; + return import_context( ¶ms ); +} + static NTSTATUS wow64_make_signature( void *args ) { struct @@ -1476,7 +1571,9 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = wow64_accept_context, wow64_acquire_credentials_handle, wow64_delete_context, + wow64_export_context, wow64_free_credentials_handle, + wow64_import_context, wow64_initialize_context, wow64_make_signature, wow64_query_context_attributes, diff --git a/dlls/kerberos/unixlib.h b/dlls/kerberos/unixlib.h index 60a8c2f243c..be48b1595fb 100644 --- a/dlls/kerberos/unixlib.h +++ b/dlls/kerberos/unixlib.h @@ -51,11 +51,25 @@ struct delete_context_params UINT64 context; }; +struct export_context_params +{ + UINT64 *context; + BYTE *buf; + ULONG *size; +}; + struct free_credentials_handle_params { UINT64 credential; }; +struct import_context_params +{ + BYTE *buf; + ULONG size; + UINT64 *context; +}; + struct initialize_context_params { UINT64 credential; @@ -131,7 +145,9 @@ enum unix_funcs unix_accept_context, unix_acquire_credentials_handle, unix_delete_context, + unix_export_context, unix_free_credentials_handle, + unix_import_context, unix_initialize_context, unix_make_signature, unix_query_context_attributes, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11729