Ensured to work with both the current `klist` implementation and the one modified to use KerbQueryTicketCacheExMessage. Patch for `klist` will be sent separately.
-- v2: dlls/kerberos: Implement KerbQueryTicketCacheExMessage.
From: Maxim Karasev mxkrsv@etersoft.ru
--- dlls/kerberos/krb5_ap.c | 97 +++++++++++++++++++++++++++++++++++++++-- dlls/kerberos/unixlib.c | 95 +++++++++++++++++++++++++--------------- dlls/kerberos/unixlib.h | 2 +- 3 files changed, 153 insertions(+), 41 deletions(-)
diff --git a/dlls/kerberos/krb5_ap.c b/dlls/kerberos/krb5_ap.c index 42d3358a20d..3352b20b188 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,7 @@ 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 ); 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 +305,27 @@ 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 ); + 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..1696cf646f5 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,20 @@ 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) +{ + *name = strtok( name_with_realm, "@" ); + TRACE( "name without realm: %s\n", debugstr_a(*name) ); + + *realm = strtok( NULL, "@" ); + if (!*realm) + { + /* point it to a null byte */ + *realm = *name + strlen(*name); + } + TRACE( "realm: %s\n", debugstr_a(*realm) ); +} + static NTSTATUS copy_tickets_from_cache( krb5_context ctx, krb5_ccache cache, struct ticket_list *list ) { NTSTATUS status; @@ -175,7 +189,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 +213,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 +224,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) );
- if ((err = p_krb5_unparse_name_flags( ctx, creds.server, KRB5_PRINCIPAL_UNPARSE_NO_REALM, - &name_without_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.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) ); - - utf8_to_wstr( &list->tickets[list->count].ServerName, name_without_realm ); + TRACE( "client_name_with_realm: %s\n", debugstr_a(client_name_with_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 +261,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 +279,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 +306,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 +364,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 +1194,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 +1207,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 +1223,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 +1249,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; };
Hans Leidekker (@hans) commented about dlls/kerberos/krb5_ap.c:
*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 ); struct query_ticket_cache_params params = { resp, out_buf_len }; status = KRB5_CALL( query_ticket_cache, ¶ms );
While you're at it, please check for allocation failure here and below.
Hans Leidekker (@hans) commented about dlls/kerberos/unixlib.c:
strW->Length = dstlen - sizeof(WCHAR);
}
+static void principal_to_name_and_realm(char *name_with_realm, char **name, char **realm) +{
- *name = strtok( name_with_realm, "@" );
strtok() should be avoided, it's not thread-safe.