kerberos: Move the ticket cache memory allocation to the PE side.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-11-04 17:37:59 +01:00
parent b2009d02c3
commit de53f5682f
3 changed files with 109 additions and 124 deletions

View File

@ -111,91 +111,47 @@ static NTSTATUS NTAPI kerberos_LsaApInitializePackage(ULONG package_id, PLSA_DIS
return STATUS_SUCCESS;
}
static void free_ticket_list( struct ticket_list *list )
{
ULONG i;
for (i = 0; i < list->count; i++)
{
RtlFreeHeap( GetProcessHeap(), 0, list->tickets[i].RealmName.Buffer );
RtlFreeHeap( GetProcessHeap(), 0, list->tickets[i].ServerName.Buffer );
}
RtlFreeHeap( GetProcessHeap(), 0, list->tickets );
}
static inline void init_client_us(UNICODE_STRING *dst, void *client_ws, const UNICODE_STRING *src)
{
dst->Buffer = client_ws;
dst->Length = src->Length;
dst->MaximumLength = src->MaximumLength;
}
static NTSTATUS copy_to_client(PLSA_CLIENT_REQUEST lsa_req, struct ticket_list *list, void **out, ULONG *out_size)
static NTSTATUS copy_to_client( PLSA_CLIENT_REQUEST lsa_req, KERB_QUERY_TKT_CACHE_RESPONSE *resp,
void **out, ULONG size )
{
NTSTATUS status;
ULONG i;
SIZE_T size, client_str_off;
char *client_resp, *client_ticket, *client_str;
KERB_QUERY_TKT_CACHE_RESPONSE resp;
char *client_str;
KERB_QUERY_TKT_CACHE_RESPONSE *client_resp;
size = sizeof(resp);
if (list->count) size += (list->count - 1) * sizeof(KERB_TICKET_CACHE_INFO);
client_str_off = size;
for (i = 0; i < list->count; i++)
{
size += list->tickets[i].RealmName.MaximumLength;
size += list->tickets[i].ServerName.MaximumLength;
}
status = lsa_dispatch.AllocateClientBuffer(lsa_req, size, (void **)&client_resp);
status = lsa_dispatch.AllocateClientBuffer(lsa_req, size, out );
if (status != STATUS_SUCCESS) return status;
resp.MessageType = KerbQueryTicketCacheMessage;
resp.CountOfTickets = list->count;
size = FIELD_OFFSET(KERB_QUERY_TKT_CACHE_RESPONSE, Tickets);
status = lsa_dispatch.CopyToClientBuffer(lsa_req, size, client_resp, &resp);
client_resp = *out;
status = lsa_dispatch.CopyToClientBuffer(lsa_req, offsetof(KERB_QUERY_TKT_CACHE_RESPONSE, Tickets),
client_resp, resp);
if (status != STATUS_SUCCESS) goto fail;
if (!list->count)
client_str = (char *)&client_resp->Tickets[resp->CountOfTickets];
for (i = 0; i < resp->CountOfTickets; i++)
{
*out = client_resp;
*out_size = sizeof(resp);
return STATUS_SUCCESS;
KERB_TICKET_CACHE_INFO ticket = resp->Tickets[i];
RtlSecondsSince1970ToTime( resp->Tickets[i].StartTime.QuadPart, &ticket.StartTime );
RtlSecondsSince1970ToTime( resp->Tickets[i].EndTime.QuadPart, &ticket.EndTime );
RtlSecondsSince1970ToTime( resp->Tickets[i].RenewTime.QuadPart, &ticket.RenewTime );
status = lsa_dispatch.CopyToClientBuffer(lsa_req, ticket.RealmName.MaximumLength,
client_str, ticket.RealmName.Buffer);
if (status != STATUS_SUCCESS) goto fail;
ticket.RealmName.Buffer = (WCHAR *)client_str;
client_str += ticket.RealmName.MaximumLength;
status = lsa_dispatch.CopyToClientBuffer(lsa_req, ticket.ServerName.MaximumLength,
client_str, ticket.ServerName.Buffer);
if (status != STATUS_SUCCESS) goto fail;
ticket.ServerName.Buffer = (WCHAR *)client_str;
client_str += ticket.ServerName.MaximumLength;
status = lsa_dispatch.CopyToClientBuffer(lsa_req, sizeof(ticket), &client_resp->Tickets[i], &ticket);
if (status != STATUS_SUCCESS) goto fail;
}
*out_size = size;
client_ticket = client_resp + size;
client_str = client_resp + client_str_off;
for (i = 0; i < list->count; i++)
{
KERB_TICKET_CACHE_INFO ticket = list->tickets[i];
init_client_us(&ticket.RealmName, client_str, &list->tickets[i].RealmName);
size = ticket.RealmName.MaximumLength;
status = lsa_dispatch.CopyToClientBuffer(lsa_req, size, client_str, list->tickets[i].RealmName.Buffer);
if (status != STATUS_SUCCESS) goto fail;
client_str += size;
*out_size += size;
init_client_us(&ticket.ServerName, client_str, &list->tickets[i].ServerName);
size = ticket.ServerName.MaximumLength;
status = lsa_dispatch.CopyToClientBuffer(lsa_req, size, client_str, list->tickets[i].ServerName.Buffer);
if (status != STATUS_SUCCESS) goto fail;
client_str += size;
*out_size += size;
status = lsa_dispatch.CopyToClientBuffer(lsa_req, sizeof(ticket), client_ticket, &ticket);
if (status != STATUS_SUCCESS) goto fail;
client_ticket += sizeof(ticket);
*out_size += sizeof(ticket);
}
*out = client_resp;
return STATUS_SUCCESS;
fail:
@ -218,17 +174,19 @@ static NTSTATUS NTAPI kerberos_LsaApCallPackageUntrusted(PLSA_CLIENT_REQUEST req
case KerbQueryTicketCacheMessage:
{
KERB_QUERY_TKT_CACHE_REQUEST *query = (KERB_QUERY_TKT_CACHE_REQUEST *)in_buf;
struct ticket_list list;
NTSTATUS status;
if (!in_buf || in_buf_len != sizeof(*query) || !out_buf || !out_buf_len) return STATUS_INVALID_PARAMETER;
if (query->LogonId.HighPart || query->LogonId.LowPart) return STATUS_ACCESS_DENIED;
status = krb5_funcs->query_ticket_cache(&list);
if (!status)
*out_buf_len = 1024;
for (;;)
{
status = copy_to_client(req, &list, out_buf, out_buf_len);
free_ticket_list(&list);
KERB_QUERY_TKT_CACHE_RESPONSE *resp = malloc( *out_buf_len );
status = krb5_funcs->query_ticket_cache( resp, out_buf_len );
if (status == STATUS_SUCCESS) status = copy_to_client( req, resp, out_buf, *out_buf_len );
free( resp );
if (status != STATUS_BUFFER_TOO_SMALL) break;
}
*ret_status = status;
break;

View File

@ -151,15 +151,21 @@ static NTSTATUS krb5_error_to_status( krb5_error_code err )
}
}
static WCHAR *utf8_to_wstr( const char *src )
struct ticket_list
{
ULONG count;
ULONG allocated;
KERB_TICKET_CACHE_INFO *tickets;
};
static void utf8_to_wstr( UNICODE_STRING *strW, const char *src )
{
ULONG dstlen, srclen = strlen( src ) + 1;
WCHAR *dst;
RtlUTF8ToUnicodeN( NULL, 0, &dstlen, src, srclen );
if ((dst = RtlAllocateHeap( GetProcessHeap(), 0, dstlen )))
RtlUTF8ToUnicodeN( dst, dstlen, &dstlen, src, srclen );
return dst;
strW->Buffer = malloc( srclen * sizeof(WCHAR) );
RtlUTF8ToUnicodeN( strW->Buffer, srclen * sizeof(WCHAR), &dstlen, src, srclen );
strW->MaximumLength = dstlen;
strW->Length = dstlen - sizeof(WCHAR);
}
static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, struct ticket_list *list )
@ -170,7 +176,6 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st
krb5_creds creds;
krb5_ticket *ticket;
char *name_with_realm, *name_without_realm, *realm_name;
WCHAR *realm_nameW, *name_without_realmW;
if ((err = p_krb5_cc_start_seq_get( ctx, cache, &cursor ))) return krb5_error_to_status( err );
for (;;)
@ -192,19 +197,8 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st
if (list->count == list->allocated)
{
KERB_TICKET_CACHE_INFO *new_tickets;
ULONG new_allocated;
if (list->allocated)
{
new_allocated = list->allocated * 2;
new_tickets = RtlReAllocateHeap( GetProcessHeap(), 0, list->tickets, sizeof(*new_tickets) * new_allocated );
}
else
{
new_allocated = 16;
new_tickets = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*new_tickets) * new_allocated );
}
ULONG new_allocated = max( 16, list->allocated * 2 );
KERB_TICKET_CACHE_INFO *new_tickets = realloc( list->tickets, sizeof(*new_tickets) * new_allocated );
if (!new_tickets)
{
p_krb5_free_cred_contents( ctx, &creds );
@ -233,8 +227,7 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st
}
TRACE( "name_without_realm: %s\n", debugstr_a(name_without_realm) );
name_without_realmW = utf8_to_wstr( name_without_realm );
RtlInitUnicodeString( &list->tickets[list->count].ServerName, name_without_realmW );
utf8_to_wstr( &list->tickets[list->count].ServerName, name_without_realm );
if (!(realm_name = strchr( name_with_realm, '@' )))
{
@ -244,16 +237,16 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st
else realm_name++;
/* realm_name - now contains only realm! */
realm_nameW = utf8_to_wstr( realm_name );
RtlInitUnicodeString( &list->tickets[list->count].RealmName, realm_nameW );
utf8_to_wstr( &list->tickets[list->count].RealmName, realm_name );
if (!creds.times.starttime) creds.times.starttime = creds.times.authtime;
/* TODO: if krb5_is_config_principal = true */
RtlSecondsSince1970ToTime( creds.times.starttime, &list->tickets[list->count].StartTime );
RtlSecondsSince1970ToTime( creds.times.endtime, &list->tickets[list->count].EndTime );
RtlSecondsSince1970ToTime( creds.times.renew_till, &list->tickets[list->count].RenewTime );
/* note: store times as seconds, they will be converted to NT timestamps on the PE side */
list->tickets[list->count].StartTime.QuadPart = creds.times.starttime;
list->tickets[list->count].EndTime.QuadPart = creds.times.endtime;
list->tickets[list->count].RenewTime.QuadPart = creds.times.renew_till;
list->tickets[list->count].TicketFlags = creds.ticket_flags;
err = p_krb5_decode_ticket( &creds.ticket, &ticket );
@ -275,17 +268,50 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st
return status;
}
static NTSTATUS CDECL query_ticket_cache( struct ticket_list *list )
static NTSTATUS copy_tickets_to_client( struct ticket_list *list, KERB_QUERY_TKT_CACHE_RESPONSE *resp,
ULONG *out_size )
{
char *client_str;
ULONG i, size = offsetof( KERB_QUERY_TKT_CACHE_RESPONSE, Tickets[list->count] );
for (i = 0; i < list->count; i++)
{
size += list->tickets[i].RealmName.MaximumLength;
size += list->tickets[i].ServerName.MaximumLength;
}
if (!resp || size > *out_size)
{
*out_size = size;
return STATUS_BUFFER_TOO_SMALL;
}
*out_size = size;
resp->MessageType = KerbQueryTicketCacheMessage;
resp->CountOfTickets = list->count;
memcpy( resp->Tickets, list->tickets, list->count * sizeof(list->tickets[0]) );
client_str = (char *)&resp->Tickets[list->count];
for (i = 0; i < list->count; i++)
{
resp->Tickets[i].RealmName.Buffer = (WCHAR *)client_str;
memcpy( client_str, list->tickets[i].RealmName.Buffer, list->tickets[i].RealmName.MaximumLength );
client_str += list->tickets[i].RealmName.MaximumLength;
resp->Tickets[i].ServerName.Buffer = (WCHAR *)client_str;
memcpy( client_str, list->tickets[i].ServerName.Buffer, list->tickets[i].ServerName.MaximumLength );
client_str += list->tickets[i].ServerName.MaximumLength;
}
return STATUS_SUCCESS;
}
static NTSTATUS CDECL query_ticket_cache( KERB_QUERY_TKT_CACHE_RESPONSE *resp, ULONG *out_size )
{
NTSTATUS status;
krb5_error_code err;
krb5_context ctx;
krb5_cccol_cursor cursor = NULL;
krb5_ccache cache;
list->count = 0;
list->allocated = 0;
list->tickets = NULL;
ULONG i;
struct ticket_list list = { 0 };
if ((err = p_krb5_init_context( &ctx ))) return krb5_error_to_status( err );
if ((err = p_krb5_cccol_cursor_new( ctx, &cursor )))
@ -303,7 +329,7 @@ static NTSTATUS CDECL query_ticket_cache( struct ticket_list *list )
}
if (!cache) break;
status = copy_tickets_from_cache( ctx, cache, list );
status = copy_tickets_from_cache( ctx, cache, &list );
p_krb5_cc_close( ctx, cache );
if (status != STATUS_SUCCESS) goto done;
}
@ -311,6 +337,14 @@ static NTSTATUS CDECL query_ticket_cache( struct ticket_list *list )
done:
if (cursor) p_krb5_cccol_cursor_free( ctx, &cursor );
if (ctx) p_krb5_free_context( ctx );
if (status == STATUS_SUCCESS) status = copy_tickets_to_client( &list, resp, out_size );
for (i = 0; i < list.count; i++)
{
free( list.tickets[i].RealmName.Buffer );
free( list.tickets[i].ServerName.Buffer );
}
return status;
}
@ -949,12 +983,12 @@ static NTSTATUS unseal_message_no_vector( gss_ctx_id_t ctx, SecBufferDesc *msg,
len_token = msg->pBuffers[token_idx].cbBuffer;
input.length = len_data + len_token;
if (!(input.value = RtlAllocateHeap( GetProcessHeap(), 0, input.length ))) return SEC_E_INSUFFICIENT_MEMORY;
if (!(input.value = malloc( input.length ))) return SEC_E_INSUFFICIENT_MEMORY;
memcpy( input.value, msg->pBuffers[data_idx].pvBuffer, len_data );
memcpy( (char *)input.value + len_data, msg->pBuffers[token_idx].pvBuffer, len_token );
ret = pgss_unwrap( &minor_status, ctx, &input, &output, &conf_state, NULL );
RtlFreeHeap( GetProcessHeap(), 0, input.value );
free( input.value );
TRACE( "gss_unwrap returned %08x minor status %08x\n", ret, minor_status );
if (GSS_ERROR( ret )) trace_gss_status( ret, minor_status );
if (ret == GSS_S_COMPLETE)

View File

@ -21,13 +21,6 @@
#define KERBEROS_MAX_BUF 12000
struct ticket_list
{
ULONG count;
ULONG allocated;
KERB_TICKET_CACHE_INFO *tickets;
};
struct krb5_funcs
{
NTSTATUS (CDECL *accept_context)(LSA_SEC_HANDLE, LSA_SEC_HANDLE, SecBufferDesc *, LSA_SEC_HANDLE *,
@ -40,7 +33,7 @@ struct krb5_funcs
LSA_SEC_HANDLE *, SecBufferDesc *, ULONG *, TimeStamp *);
NTSTATUS (CDECL *make_signature)(LSA_SEC_HANDLE, SecBufferDesc *);
NTSTATUS (CDECL *query_context_attributes)(LSA_SEC_HANDLE, ULONG, void *);
NTSTATUS (CDECL *query_ticket_cache)(struct ticket_list *);
NTSTATUS (CDECL *query_ticket_cache)( KERB_QUERY_TKT_CACHE_RESPONSE *resp, ULONG *out_size );
NTSTATUS (CDECL *seal_message)(LSA_SEC_HANDLE, SecBufferDesc *, ULONG);
NTSTATUS (CDECL *unseal_message)(LSA_SEC_HANDLE, SecBufferDesc *, ULONG *);
NTSTATUS (CDECL *verify_signature)(LSA_SEC_HANDLE, SecBufferDesc *, ULONG *);