>From e89765065f18c4e9033c220a3262bbc486c59602 Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:36:36 +0300
Subject: [PATCH 5/8] kerberos: fix KerbQueryTicketCacheMessage to return all
 tickets in all ccaches

---
 wine/dlls/kerberos/krb5_ap.c | 53 +++++++++++++++++++++++++++++++-------------
 1 file changed, 38 insertions(+), 15 deletions(-)

diff --git a/wine/dlls/kerberos/krb5_ap.c b/wine/dlls/kerberos/krb5_ap.c
index cb9408b..30b9aa6 100644
--- a/wine/dlls/kerberos/krb5_ap.c
+++ b/wine/dlls/kerberos/krb5_ap.c
@@ -51,6 +51,7 @@ MAKE_FUNCPTR(krb5_init_context);
 MAKE_FUNCPTR(krb5_free_context);
 MAKE_FUNCPTR(krb5_cccol_cursor_new);
 MAKE_FUNCPTR(krb5_cccol_cursor_next);
+MAKE_FUNCPTR(krb5_cccol_cursor_free);
 MAKE_FUNCPTR(krb5_cc_close);
 MAKE_FUNCPTR(krb5_cc_start_seq_get);
 MAKE_FUNCPTR(krb5_cc_end_seq_get);
@@ -99,6 +100,7 @@ static BOOL load_krb5(void)
     LOAD_FUNCPTR(krb5_free_context)
     LOAD_FUNCPTR(krb5_cccol_cursor_new)
     LOAD_FUNCPTR(krb5_cccol_cursor_next)
+    LOAD_FUNCPTR(krb5_cccol_cursor_free)
     LOAD_FUNCPTR(krb5_cc_close)
     LOAD_FUNCPTR(krb5_cc_start_seq_get)
     LOAD_FUNCPTR(krb5_cc_end_seq_get)
@@ -151,7 +153,6 @@ static NTSTATUS krb5_error_to_status(krb5_error_code error)
     switch (error)
     {
     case 0: return STATUS_SUCCESS;
-
     default:
         /* FIXME */
         return STATUS_UNSUCCESSFUL;
@@ -171,7 +172,8 @@ static WCHAR *utf8_to_wstr(const char *utf8)
     return wstr;
 }
 
-static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_TICKET_CACHE_INFO *ticket_info)
+static NTSTATUS copy_tickets_info(krb5_context context, krb5_ccache cache,
+    KERB_TICKET_CACHE_INFO *tickets_info, ULONG tickets_allocated, ULONG *tickets_in_cache)
 {
     NTSTATUS status;
     krb5_cc_cursor cursor;
@@ -189,14 +191,13 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
 
     status = STATUS_SUCCESS;
 
+    *tickets_in_cache = 0;
+
     for (;;)
     {
         error = p_krb5_cc_next_cred(context, cache, &cursor, &credentials);
         if (error)
-        {
-            status = krb5_error_to_status(error);
             break;
-        }
 
         if (p_krb5_is_config_principal(context, credentials.server))
         {
@@ -204,6 +205,16 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
             continue;
         }
 
+        TRACE("credential has more tickets!\n");
+
+        (*tickets_in_cache)++;
+        if (*tickets_in_cache > tickets_allocated)
+        {
+            p_krb5_free_cred_contents(context, &credentials);
+            /* Indicate that we fill out a ticket_info buffer */
+            status = SEC_E_BUFFER_TOO_SMALL;
+        }
+
         error = p_krb5_unparse_name(context, credentials.server, &name_with_realm);
         if (error)
         {
@@ -227,7 +238,7 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
         TRACE("name_without_realm: %s\n", debugstr_a(name_without_realm));
 
         name_without_realmW = utf8_to_wstr(name_without_realm);
-        RtlInitUnicodeString(&ticket_info->ServerName, name_without_realmW);
+        RtlInitUnicodeString(&tickets_info->ServerName, name_without_realmW);
 
         realm_name = name_with_realm;
 
@@ -240,24 +251,24 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
         /* realm_name - now contains only realm! */
 
         realm_nameW = utf8_to_wstr(realm_name);
-        RtlInitUnicodeString(&ticket_info->RealmName, realm_nameW);
+        RtlInitUnicodeString(&tickets_info->RealmName, realm_nameW);
 
         if (!credentials.times.starttime)
             credentials.times.starttime = credentials.times.authtime;
 
         /* TODO: if krb5_is_config_principal = true */
-        RtlSecondsSince1970ToTime(credentials.times.starttime, &ticket_info->StartTime);
-        RtlSecondsSince1970ToTime(credentials.times.endtime, &ticket_info->EndTime);
-        RtlSecondsSince1970ToTime(credentials.times.renew_till, &ticket_info->RenewTime);
+        RtlSecondsSince1970ToTime(credentials.times.starttime, &tickets_info->StartTime);
+        RtlSecondsSince1970ToTime(credentials.times.endtime, &tickets_info->EndTime);
+        RtlSecondsSince1970ToTime(credentials.times.renew_till, &tickets_info->RenewTime);
 
-        ticket_info->TicketFlags = credentials.ticket_flags;
+        tickets_info->TicketFlags = credentials.ticket_flags;
 
         error = p_krb5_decode_ticket(&credentials.ticket, &ticket);
         if (error)
             status = krb5_error_to_status(error);
         else
         {
-            ticket_info->EncryptionType = ticket->enc_part.enctype;
+            tickets_info->EncryptionType = ticket->enc_part.enctype;
             status = STATUS_SUCCESS;
         }
 
@@ -265,7 +276,8 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
         p_krb5_free_unparsed_name(context, name_without_realm);
         p_krb5_free_cred_contents(context, &credentials);
 
-        break;
+        // fill next ticket in ccache
+        tickets_info++;
     }
 
     p_krb5_cc_end_seq_get(context, cache, &cursor);
@@ -279,6 +291,7 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
     KERB_QUERY_TKT_CACHE_REQUEST *query;
     KERB_QUERY_TKT_CACHE_RESPONSE *resp;
     ULONG tickets_allocated;
+    ULONG tickets_in_cache;
     krb5_error_code error;
     krb5_context context = NULL;
     krb5_cccol_cursor cursor;
@@ -322,6 +335,8 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
             break;
         }
 
+        TRACE("Found another ccache!\n");
+
         if (resp->CountOfTickets == tickets_allocated)
         {
             KERB_QUERY_TKT_CACHE_RESPONSE *new_resp;
@@ -338,15 +353,22 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
             resp = new_resp;
         }
 
-        status = copy_ticket_info(context, cache, &resp->Tickets[resp->CountOfTickets]);
+        tickets_in_cache = 0;
+        status = copy_tickets_info(context, cache, &resp->Tickets[resp->CountOfTickets],
+                tickets_allocated - resp->CountOfTickets, &tickets_in_cache);
+
         if (status == STATUS_SUCCESS)
-            resp->CountOfTickets++;
+            resp->CountOfTickets += tickets_in_cache;
+        else if (status == STATUS_BUFFER_TOO_SMALL)
+            FIXME("Too many tickets in ccache, some tickets may be ommited\n");
 
         p_krb5_cc_close(context, cache);
     }
 
     if (status) goto done;
 
+    p_krb5_cccol_cursor_free(context, &cursor);
+
     /* FIXME: copy server/realm names to client space as well.
      * As long as LSA works in current process space it's OK.
      */
@@ -364,6 +386,7 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
 done:
     if (context)
         p_krb5_free_context(context);
+
     heap_free(resp);
     return status;
 }
-- 
2.10.4

