Ensured to work with both the current `klist` implementation and the one modified to use KerbQueryTicketCacheExMessage. Patch for `klist` will be sent separately.
-- v4: dlls/kerberos: Implement KerbQueryTicketCacheExMessage.
From: Maxim Karasev mxkrsv@etersoft.ru
--- dlls/kerberos/krb5_ap.c | 107 ++++++++++++++++++++++++++++++++++++++-- dlls/kerberos/unixlib.c | 100 +++++++++++++++++++++++-------------- dlls/kerberos/unixlib.h | 2 +- 3 files changed, 168 insertions(+), 41 deletions(-)
diff --git a/dlls/kerberos/krb5_ap.c b/dlls/kerberos/krb5_ap.c index 42d3358a20d..47519db8639 100644 --- a/dlls/kerberos/krb5_ap.c +++ b/dlls/kerberos/krb5_ap.c @@ -156,7 +156,7 @@ static NTSTATUS NTAPI kerberos_LsaApInitializePackage(ULONG package_id, PLSA_DIS return STATUS_SUCCESS; }
-static NTSTATUS copy_to_client( PLSA_CLIENT_REQUEST lsa_req, KERB_QUERY_TKT_CACHE_RESPONSE *resp, +static NTSTATUS copy_to_client( PLSA_CLIENT_REQUEST lsa_req, KERB_QUERY_TKT_CACHE_EX_RESPONSE *resp, void **out, ULONG size ) { NTSTATUS status; @@ -164,7 +164,7 @@ static NTSTATUS copy_to_client( PLSA_CLIENT_REQUEST lsa_req, KERB_QUERY_TKT_CACH char *client_str; KERB_QUERY_TKT_CACHE_RESPONSE *client_resp;
- status = lsa_dispatch.AllocateClientBuffer(lsa_req, size, out ); + status = lsa_dispatch.AllocateClientBuffer( lsa_req, size, out ); if (status != STATUS_SUCCESS) return status;
client_resp = *out; @@ -176,7 +176,15 @@ static NTSTATUS copy_to_client( PLSA_CLIENT_REQUEST lsa_req, KERB_QUERY_TKT_CACH
for (i = 0; i < resp->CountOfTickets; i++) { - KERB_TICKET_CACHE_INFO ticket = resp->Tickets[i]; + KERB_TICKET_CACHE_INFO ticket = { + .ServerName = resp->Tickets[i].ServerName, + .RealmName = resp->Tickets[i].ServerRealm, + .StartTime = resp->Tickets[i].StartTime, + .EndTime = resp->Tickets[i].EndTime, + .RenewTime = resp->Tickets[i].RenewTime, + .EncryptionType = resp->Tickets[i].EncryptionType, + .TicketFlags = resp->Tickets[i].TicketFlags, + };
RtlSecondsSince1970ToTime( resp->Tickets[i].StartTime.QuadPart, &ticket.StartTime ); RtlSecondsSince1970ToTime( resp->Tickets[i].EndTime.QuadPart, &ticket.EndTime ); @@ -204,6 +212,66 @@ fail: return status; }
+static NTSTATUS copy_to_client_ex( PLSA_CLIENT_REQUEST lsa_req, KERB_QUERY_TKT_CACHE_EX_RESPONSE *resp, + void **out, ULONG size ) +{ + NTSTATUS status; + ULONG i; + char *client_str; + KERB_QUERY_TKT_CACHE_EX_RESPONSE *client_resp; + + status = lsa_dispatch.AllocateClientBuffer( lsa_req, size, out ); + if (status != STATUS_SUCCESS) return status; + + client_resp = *out; + status = lsa_dispatch.CopyToClientBuffer(lsa_req, offsetof(KERB_QUERY_TKT_CACHE_EX_RESPONSE, Tickets), + client_resp, resp); + if (status != STATUS_SUCCESS) goto fail; + + client_str = (char *)&client_resp->Tickets[resp->CountOfTickets]; + + for (i = 0; i < resp->CountOfTickets; i++) + { + KERB_TICKET_CACHE_INFO_EX 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.ClientRealm.MaximumLength, + client_str, ticket.ClientRealm.Buffer); + if (status != STATUS_SUCCESS) goto fail; + ticket.ClientRealm.Buffer = (WCHAR *)client_str; + client_str += ticket.ClientRealm.MaximumLength; + + status = lsa_dispatch.CopyToClientBuffer(lsa_req, ticket.ClientName.MaximumLength, + client_str, ticket.ClientName.Buffer); + if (status != STATUS_SUCCESS) goto fail; + ticket.ClientName.Buffer = (WCHAR *)client_str; + client_str += ticket.ClientName.MaximumLength; + + status = lsa_dispatch.CopyToClientBuffer(lsa_req, ticket.ServerRealm.MaximumLength, + client_str, ticket.ServerRealm.Buffer); + if (status != STATUS_SUCCESS) goto fail; + ticket.ServerRealm.Buffer = (WCHAR *)client_str; + client_str += ticket.ServerRealm.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; + } + return STATUS_SUCCESS; + +fail: + lsa_dispatch.FreeClientBuffer(lsa_req, client_resp); + return status; +} + static NTSTATUS NTAPI kerberos_LsaApCallPackageUntrusted(PLSA_CLIENT_REQUEST req, void *in_buf, void *client_buf_base, ULONG in_buf_len, void **out_buf, ULONG *out_buf_len, NTSTATUS *ret_status) { @@ -227,7 +295,12 @@ static NTSTATUS NTAPI kerberos_LsaApCallPackageUntrusted(PLSA_CLIENT_REQUEST req *out_buf_len = 1024; for (;;) { - KERB_QUERY_TKT_CACHE_RESPONSE *resp = malloc( *out_buf_len ); + KERB_QUERY_TKT_CACHE_EX_RESPONSE *resp = malloc( *out_buf_len ); + if (!resp) + { + status = STATUS_NO_MEMORY; + break; + } struct query_ticket_cache_params params = { resp, out_buf_len }; status = KRB5_CALL( query_ticket_cache, ¶ms ); if (status == STATUS_SUCCESS) status = copy_to_client( req, resp, out_buf, *out_buf_len ); @@ -237,6 +310,32 @@ static NTSTATUS NTAPI kerberos_LsaApCallPackageUntrusted(PLSA_CLIENT_REQUEST req *ret_status = status; break; } + case KerbQueryTicketCacheExMessage: + { + KERB_QUERY_TKT_CACHE_REQUEST *query = (KERB_QUERY_TKT_CACHE_REQUEST *)in_buf; + 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; + + *out_buf_len = 1024; + for (;;) + { + KERB_QUERY_TKT_CACHE_EX_RESPONSE *resp = malloc( *out_buf_len ); + if (!resp) + { + status = STATUS_NO_MEMORY; + break; + } + struct query_ticket_cache_params params = { resp, out_buf_len }; + status = KRB5_CALL( query_ticket_cache, ¶ms ); + if (status == STATUS_SUCCESS) status = copy_to_client_ex( req, resp, out_buf, *out_buf_len ); + free( resp ); + if (status != STATUS_BUFFER_TOO_SMALL) break; + } + *ret_status = status; + break; + } case KerbRetrieveTicketMessage: FIXME("KerbRetrieveTicketMessage stub\n"); *ret_status = STATUS_NOT_IMPLEMENTED; diff --git a/dlls/kerberos/unixlib.c b/dlls/kerberos/unixlib.c index 8d9b59b58d4..121809e231b 100644 --- a/dlls/kerberos/unixlib.c +++ b/dlls/kerberos/unixlib.c @@ -155,7 +155,7 @@ struct ticket_list { ULONG count; ULONG allocated; - KERB_TICKET_CACHE_INFO *tickets; + KERB_TICKET_CACHE_INFO_EX *tickets; };
static void utf8_to_wstr( UNICODE_STRING *strW, const char *src ) @@ -168,6 +168,25 @@ static void utf8_to_wstr( UNICODE_STRING *strW, const char *src ) strW->Length = dstlen - sizeof(WCHAR); }
+static void principal_to_name_and_realm(char *name_with_realm, char **name, char **realm) +{ + char* separator; + + separator = strchr( name_with_realm, '@' ); + if (!separator) + { + ERR( "got a name without a @\n" ); + *name = name_with_realm; + *realm = *name + strlen(*name); /* point it to a null byte */ + return; + } + + *separator = '\0'; + *name = name_with_realm; + *realm = separator + 1; /* character after a @ */ + TRACE( "name: %s, realm: %s\n", debugstr_a(*name), debugstr_a(*realm) ); +} + static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, struct ticket_list *list ) { NTSTATUS status; @@ -175,7 +194,8 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st krb5_error_code err; krb5_creds creds; krb5_ticket *ticket; - char *name_with_realm, *name_without_realm, *realm_name; + char *server_name_with_realm, *server_name, *server_realm; + char *client_name_with_realm, *client_name, *client_realm;
if ((err = p_krb5_cc_start_seq_get( ctx, cache, &cursor ))) return krb5_error_to_status( err ); for (;;) @@ -198,7 +218,7 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st if (list->count == list->allocated) { ULONG new_allocated = max( 16, list->allocated * 2 ); - KERB_TICKET_CACHE_INFO *new_tickets = realloc( list->tickets, sizeof(*new_tickets) * new_allocated ); + KERB_TICKET_CACHE_INFO_EX *new_tickets = realloc( list->tickets, sizeof(*new_tickets) * new_allocated ); if (!new_tickets) { p_krb5_free_cred_contents( ctx, &creds ); @@ -209,35 +229,31 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st list->allocated = new_allocated; }
- if ((err = p_krb5_unparse_name_flags( ctx, creds.server, 0, &name_with_realm ))) + if ((err = p_krb5_unparse_name_flags( ctx, creds.server, 0, &server_name_with_realm ))) { p_krb5_free_cred_contents( ctx, &creds ); status = krb5_error_to_status( err ); break; } - TRACE( "name_with_realm: %s\n", debugstr_a(name_with_realm) ); + TRACE( "server_name_with_realm: %s\n", debugstr_a(server_name_with_realm) ); + + principal_to_name_and_realm( server_name_with_realm, &server_name, &server_realm ); + + utf8_to_wstr( &list->tickets[list->count].ServerName, server_name ); + utf8_to_wstr( &list->tickets[list->count].ServerRealm, server_realm );
- if ((err = p_krb5_unparse_name_flags( ctx, creds.server, KRB5_PRINCIPAL_UNPARSE_NO_REALM, - &name_without_realm ))) + if ((err = p_krb5_unparse_name_flags( ctx, creds.client, 0, &client_name_with_realm ))) { - p_krb5_free_unparsed_name( ctx, name_with_realm ); p_krb5_free_cred_contents( ctx, &creds ); status = krb5_error_to_status( err ); break; } - TRACE( "name_without_realm: %s\n", debugstr_a(name_without_realm) ); + TRACE( "client_name_with_realm: %s\n", debugstr_a(client_name_with_realm) );
- utf8_to_wstr( &list->tickets[list->count].ServerName, name_without_realm ); - - if (!(realm_name = strchr( name_with_realm, '@' ))) - { - ERR( "wrong name with realm %s\n", debugstr_a(name_with_realm) ); - realm_name = name_with_realm; - } - else realm_name++; + principal_to_name_and_realm( client_name_with_realm, &client_name, &client_realm );
- /* realm_name - now contains only realm! */ - utf8_to_wstr( &list->tickets[list->count].RealmName, realm_name ); + utf8_to_wstr( &list->tickets[list->count].ClientName, client_name ); + utf8_to_wstr( &list->tickets[list->count].ClientRealm, client_realm );
if (!creds.times.starttime) creds.times.starttime = creds.times.authtime;
@@ -250,8 +266,8 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st list->tickets[list->count].TicketFlags = creds.ticket_flags;
err = p_krb5_decode_ticket( &creds.ticket, &ticket ); - p_krb5_free_unparsed_name( ctx, name_with_realm ); - p_krb5_free_unparsed_name( ctx, name_without_realm ); + p_krb5_free_unparsed_name( ctx, server_name_with_realm ); + p_krb5_free_unparsed_name( ctx, client_name_with_realm ); p_krb5_free_cred_contents( ctx, &creds ); if (err) { @@ -268,15 +284,17 @@ static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, st return status; }
-static NTSTATUS copy_tickets_to_client( struct ticket_list *list, KERB_QUERY_TKT_CACHE_RESPONSE *resp, +static NTSTATUS copy_tickets_to_client( struct ticket_list *list, KERB_QUERY_TKT_CACHE_EX_RESPONSE *resp, ULONG *out_size ) { char *client_str; - ULONG i, size = offsetof( KERB_QUERY_TKT_CACHE_RESPONSE, Tickets[list->count] ); + ULONG i, size = offsetof( KERB_QUERY_TKT_CACHE_EX_RESPONSE, Tickets[list->count] );
for (i = 0; i < list->count; i++) { - size += list->tickets[i].RealmName.MaximumLength; + size += list->tickets[i].ClientRealm.MaximumLength; + size += list->tickets[i].ClientName.MaximumLength; + size += list->tickets[i].ServerRealm.MaximumLength; size += list->tickets[i].ServerName.MaximumLength; } if (!resp || size > *out_size) @@ -293,9 +311,15 @@ static NTSTATUS copy_tickets_to_client( struct ticket_list *list, KERB_QUERY_TKT
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].ClientRealm.Buffer = (WCHAR *)client_str; + memcpy( client_str, list->tickets[i].ClientRealm.Buffer, list->tickets[i].ClientRealm.MaximumLength ); + client_str += list->tickets[i].ClientRealm.MaximumLength; + resp->Tickets[i].ClientName.Buffer = (WCHAR *)client_str; + memcpy( client_str, list->tickets[i].ClientName.Buffer, list->tickets[i].ClientName.MaximumLength ); + client_str += list->tickets[i].ClientName.MaximumLength; + resp->Tickets[i].ServerRealm.Buffer = (WCHAR *)client_str; + memcpy( client_str, list->tickets[i].ServerRealm.Buffer, list->tickets[i].ServerRealm.MaximumLength ); + client_str += list->tickets[i].ServerRealm.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; @@ -345,7 +369,9 @@ static void free_tickets_in_list( struct ticket_list *list )
for (i = 0; i < list->count; i++) { - free( list->tickets[i].RealmName.Buffer ); + free( list->tickets[i].ClientRealm.Buffer ); + free( list->tickets[i].ClientName.Buffer ); + free( list->tickets[i].ServerRealm.Buffer ); free( list->tickets[i].ServerName.Buffer ); }
@@ -1173,10 +1199,12 @@ static NTSTATUS wow64_query_context_attributes( void *args ) return query_context_attributes( ¶ms ); }
-struct KERB_TICKET_CACHE_INFO32 +struct KERB_TICKET_CACHE_INFO_EX32 { + UNICODE_STRING32 ClientName; + UNICODE_STRING32 ClientRealm; UNICODE_STRING32 ServerName; - UNICODE_STRING32 RealmName; + UNICODE_STRING32 ServerRealm; LARGE_INTEGER StartTime; LARGE_INTEGER EndTime; LARGE_INTEGER RenewTime; @@ -1184,11 +1212,11 @@ struct KERB_TICKET_CACHE_INFO32 ULONG TicketFlags; };
-struct KERB_QUERY_TKT_CACHE_RESPONSE32 +struct KERB_QUERY_TKT_CACHE_EX_RESPONSE32 { KERB_PROTOCOL_MESSAGE_TYPE MessageType; ULONG CountOfTickets; - struct KERB_TICKET_CACHE_INFO32 Tickets[ANYSIZE_ARRAY]; + struct KERB_TICKET_CACHE_INFO_EX32 Tickets[ANYSIZE_ARRAY]; };
static void copy_ticket_ustr_64to32( const UNICODE_STRING *str, UNICODE_STRING32 *str32, ULONG *client_str ) @@ -1200,16 +1228,16 @@ static void copy_ticket_ustr_64to32( const UNICODE_STRING *str, UNICODE_STRING32 *client_str += str->MaximumLength; }
-static NTSTATUS copy_tickets_to_client32( struct ticket_list *list, struct KERB_QUERY_TKT_CACHE_RESPONSE32 *resp, +static NTSTATUS copy_tickets_to_client32( struct ticket_list *list, struct KERB_QUERY_TKT_CACHE_EX_RESPONSE32 *resp, ULONG *out_size ) { ULONG i, size, size_fixed; ULONG client_str;
- size = size_fixed = offsetof( struct KERB_QUERY_TKT_CACHE_RESPONSE32, Tickets[list->count] ); + size = size_fixed = offsetof( struct KERB_QUERY_TKT_CACHE_EX_RESPONSE32, Tickets[list->count] ); for (i = 0; i < list->count; i++) { - size += list->tickets[i].RealmName.MaximumLength; + size += list->tickets[i].ServerRealm.MaximumLength; size += list->tickets[i].ServerName.MaximumLength; } if (!resp || size > *out_size) @@ -1226,7 +1254,7 @@ static NTSTATUS copy_tickets_to_client32( struct ticket_list *list, struct KERB_ for (i = 0; i < list->count; i++) { copy_ticket_ustr_64to32( &list->tickets[i].ServerName, &resp->Tickets[i].ServerName, &client_str ); - copy_ticket_ustr_64to32( &list->tickets[i].RealmName, &resp->Tickets[i].RealmName, &client_str ); + copy_ticket_ustr_64to32( &list->tickets[i].ServerRealm, &resp->Tickets[i].ServerRealm, &client_str ); resp->Tickets[i].StartTime = list->tickets[i].StartTime; resp->Tickets[i].EndTime = list->tickets[i].EndTime; resp->Tickets[i].RenewTime = list->tickets[i].RenewTime; diff --git a/dlls/kerberos/unixlib.h b/dlls/kerberos/unixlib.h index 1725f813a16..fa83e2187f0 100644 --- a/dlls/kerberos/unixlib.h +++ b/dlls/kerberos/unixlib.h @@ -89,7 +89,7 @@ struct query_context_attributes_params
struct query_ticket_cache_params { - KERB_QUERY_TKT_CACHE_RESPONSE *resp; + KERB_QUERY_TKT_CACHE_EX_RESPONSE *resp; ULONG *out_size; };
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=135231
Your paranoid android.
=== debian11b (64 bit WoW report) ===
win32u: win32u.c:1057: Test failed: buf = win32u:win32u:01a4 done (0) in 0s 1374B
I get warnings with this version: ``` ../wine/dlls/kerberos/krb5_ap.c: In function ‘kerberos_LsaApCallPackageUntrusted’: ../wine/dlls/kerberos/krb5_ap.c:304:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] 304 | struct query_ticket_cache_params params = { resp, out_buf_len }; ```