krb5 commit: kfw support for multiple identities

Benjamin Kaduk kaduk at MIT.EDU
Fri Aug 24 12:24:14 EDT 2012


https://github.com/krb5/krb5/commit/9bc411e72fce5bed3ed00ae5b09f8c239309bae0
commit 9bc411e72fce5bed3ed00ae5b09f8c239309bae0
Author: Kevin Wasserman <kevin.wasserman at painless-security.com>
Date:   Mon Jul 23 04:30:27 2012 -0400

    kfw support for multiple identities
    
    We need a sense of what the default identity is, then, with a way
    to set it and list it.
    The memory management model changes some, as well.
    Use a bold font to indicate the current default identity in the
    GUI; while here use an italic font for expired credentials.
    
    In the process, rip out some krb4 remenants, and remove ancient
    code conditional on the lack of KRB5_TC_NOTICKET.
    
    Define USE_MESSAGE_BOX when building leash and use MessageBox().
    
    [kaduk at mit.edu: adjust for style, flesh out commit message.]
    
    ticket: 7253 (new)
    queue: kfw
    target_version: 1.10.4
    tags: pullup

 src/windows/include/leashwin.h             |   30 +-
 src/windows/leash/KrbListTickets.cpp       |  393 ++++++++
 src/windows/leash/Leash.cpp                |  153 +---
 src/windows/leash/Leash.h                  |    3 +
 src/windows/leash/Leash.rc                 |    4 +
 src/windows/leash/LeashView.cpp            | 1432 ++++++++++++++--------------
 src/windows/leash/LeashView.h              |   65 ++-
 src/windows/leash/Lglobals.h               |   43 +-
 src/windows/leash/Makefile.in              |    5 +-
 src/windows/leash/res/ribbon1.mfcribbon-ms |    2 +-
 src/windows/leash/resource.h               |    2 +-
 src/windows/leashdll/AFSroutines.c         |    3 +-
 src/windows/leashdll/krb5routines.c        |  790 +++++++--------
 src/windows/leashdll/leash-int.h           |    4 +-
 src/windows/leashdll/leashdll.h            |   15 +-
 src/windows/leashdll/leashw32.def          |    1 +
 src/windows/leashdll/lshfunc.c             |   45 +-
 17 files changed, 1658 insertions(+), 1332 deletions(-)

diff --git a/src/windows/include/leashwin.h b/src/windows/include/leashwin.h
index 6a26c43..39a7d1a 100644
--- a/src/windows/include/leashwin.h
+++ b/src/windows/include/leashwin.h
@@ -103,15 +103,29 @@ typedef struct {
         2 * NETID_CCACHE_NAME_SZ))
 #endif /* NETIDMGR */
 
-typedef struct {
-    char    principal[MAX_K_NAME_SZ]; /* Principal name/instance/realm */
+typedef struct TicketList TicketList;
+struct TicketList {
+    TicketList *next;
+    char *service;
+    char *encTypes;
+    krb5_timestamp issued;
+    krb5_timestamp valid_until;
+    krb5_timestamp renew_until;
+    unsigned long flags;
+};
+
+typedef struct TICKETINFO TICKETINFO;
+struct TICKETINFO {
+    TICKETINFO *next;
+    char   *principal;                /* Principal name/instance at realm */
+    char   *ccache_name;
+    TicketList *ticket_list;
     int     btickets;                 /* Do we have tickets? */
-    long    lifetime;                 /* Lifetime -- needs to have
-                                         room for 255 5-minute
-                                         periods * 5 * 60 */
-    long    issue_date;               /* The issue time */
-    long    renew_till;               /* The Renew time (k5 only) */
-} TICKETINFO;
+    long    issued;                   /* The issue time */
+    long    valid_until;              /* */
+    long    renew_until;              /* The Renew time (k5 only) */
+    unsigned long flags;
+};
 
 int FAR Leash_kinit_dlg(HWND hParent, LPLSH_DLGINFO lpdlginfo);
 int FAR Leash_kinit_dlg_ex(HWND hParent, LPLSH_DLGINFO_EX lpdlginfoex);
diff --git a/src/windows/leash/KrbListTickets.cpp b/src/windows/leash/KrbListTickets.cpp
new file mode 100644
index 0000000..71a4c63
--- /dev/null
+++ b/src/windows/leash/KrbListTickets.cpp
@@ -0,0 +1,393 @@
+#include "stdafx.h"
+#include "lglobals.h"
+#include "krb5.h"
+
+static void
+FreeTicketList(TicketList** ticketList)
+{
+    TicketList* tempList = *ticketList, *killList;
+
+    while (tempList) {
+        killList = tempList;
+        tempList = tempList->next;
+        free(killList->service);
+        if (killList->encTypes)
+            free(killList->encTypes);
+        free(killList);
+    }
+
+    *ticketList = NULL;
+}
+
+void
+LeashKRB5FreeTicketInfo(TICKETINFO *ticketinfo)
+{
+    if (ticketinfo->principal) {
+        free(ticketinfo->principal);
+        ticketinfo->principal = NULL;
+    }
+    if (ticketinfo->ccache_name) {
+        free(ticketinfo->ccache_name);
+        ticketinfo->ccache_name = NULL;
+    }
+    if (ticketinfo->ticket_list)
+        FreeTicketList(&ticketinfo->ticket_list);
+}
+
+void
+LeashKRB5FreeTickets(TICKETINFO **ticketinfolist)
+{
+    TICKETINFO *ticketinfo = *ticketinfolist, *next;
+    while (ticketinfo) {
+        next = ticketinfo->next;
+        LeashKRB5FreeTicketInfo(ticketinfo);
+        free(ticketinfo);
+        ticketinfo = next;
+    }
+    *ticketinfolist = NULL;
+}
+
+/*
+ * LeashKRB5Error()
+ */
+int
+LeashKRB5Error(krb5_error_code rc, LPCSTR FailedFunctionName)
+{
+#ifdef USE_MESSAGE_BOX
+    char message[256];
+    const char *errText;
+
+    errText = perror_message(rc);
+    _snprintf(message, sizeof(message),
+              "%s\n(Kerberos error %ld)\n\n%s failed",
+              errText,
+              rc,
+              FailedFunctionName);
+    message[sizeof(message)-1] = 0;
+
+    MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
+               MB_TASKMODAL |
+               MB_SETFOREGROUND);
+#endif /* USE_MESSAGE_BOX */
+    return rc;
+}
+
+
+static char *
+etype_string(krb5_enctype enctype)
+{
+    static char buf[100];
+
+    krb5_error_code retval;
+
+    if ((retval = pkrb5_enctype_to_name(enctype, FALSE, buf, sizeof(buf)))) {
+        /* XXX if there's an error != EINVAL, I should probably report it */
+        sprintf_s(buf, "etype %d", enctype);
+    }
+
+    return buf;
+}
+
+
+static void
+CredToTicketInfo(krb5_creds KRBv5Credentials, TICKETINFO *ticketinfo)
+{
+    ticketinfo->issued = KRBv5Credentials.times.starttime;
+    ticketinfo->valid_until = KRBv5Credentials.times.endtime;
+    ticketinfo->renew_until = KRBv5Credentials.ticket_flags & TKT_FLG_RENEWABLE ?
+        KRBv5Credentials.times.renew_till : 0;
+    _tzset();
+    if ( ticketinfo->valid_until - time(0) <= 0L )
+        ticketinfo->btickets = EXPD_TICKETS;
+    else
+        ticketinfo->btickets = GOOD_TICKETS;
+}
+
+static int
+CredToTicketList(krb5_context ctx, krb5_creds KRBv5Credentials,
+                 char *PrincipalName, TicketList ***ticketListTail)
+{
+    krb5_error_code code = 0;
+    krb5_ticket *tkt=NULL;
+    char *sServerName = NULL;
+    char Buffer[256];
+    char *functionName = NULL;
+    TicketList *list = NULL;
+
+    functionName = "krb5_unparse_name()";
+    code = (*pkrb5_unparse_name)(ctx, KRBv5Credentials.server, &sServerName);
+    if (code)
+        goto cleanup;
+
+    if (!KRBv5Credentials.times.starttime)
+        KRBv5Credentials.times.starttime = KRBv5Credentials.times.authtime;
+
+    memset(Buffer, '\0', sizeof(Buffer));
+
+    // @fixme: calloc for ptr init
+    list = (TicketList *)calloc(1, sizeof(TicketList));
+    if (!list) {
+        code = ENOMEM;
+        functionName = "calloc()";
+        goto cleanup;
+    }
+    list->service = strdup(sServerName);
+    if (!list->service) {
+        code = ENOMEM;
+        functionName = "calloc()";
+        goto cleanup;
+    }
+    list->issued = KRBv5Credentials.times.starttime;
+    list->valid_until = KRBv5Credentials.times.endtime;
+    if (KRBv5Credentials.ticket_flags & TKT_FLG_RENEWABLE)
+        list->renew_until = KRBv5Credentials.times.renew_till;
+    else
+        list->renew_until = 0;
+
+    if (!pkrb5_decode_ticket(&KRBv5Credentials.ticket, &tkt)) {
+        wsprintf(Buffer, "Session Key: %s  Ticket: %s",
+            etype_string(KRBv5Credentials.keyblock.enctype),
+            etype_string(tkt->enc_part.enctype));
+        pkrb5_free_ticket(ctx, tkt);
+        tkt = NULL;
+    } else {
+        wsprintf(Buffer, "Session Key: %s",
+            etype_string(KRBv5Credentials.keyblock.enctype));
+    }
+
+    list->encTypes = (char *)calloc(1, strlen(Buffer)+1);
+    if (!list->encTypes) {
+        functionName = "calloc()";
+        code = ENOMEM;
+        goto cleanup;
+    }
+    strcpy(list->encTypes, Buffer);
+
+    list->flags = KRBv5Credentials.ticket_flags;
+cleanup:
+    if (code) {
+        LeashKRB5Error(code, functionName);
+        if (list)
+            FreeTicketList(&list);
+    } else {
+        **ticketListTail = list;
+        *ticketListTail = &list->next;
+    }
+
+    if (sServerName != NULL)
+        (*pkrb5_free_unparsed_name)(ctx, sServerName);
+
+    return code;
+}
+
+// return 0 if ticketinfo was successfully appended to list, 1 otherwise
+int
+do_ccache(krb5_context ctx,
+          krb5_ccache cache,
+          TICKETINFO **ticketInfoTail)
+{
+    krb5_cc_cursor cur;
+    krb5_creds creds;
+    krb5_principal princ = NULL;
+    krb5_flags flags;
+    krb5_error_code code;
+    char *defname = NULL;
+    char *functionName = NULL;
+    TicketList **ticketListTail;
+    TICKETINFO *ticketinfo = NULL;
+    int retval = 1;
+
+    // Don't need the actual ticket, also turns off OPENCLOSE mode
+    flags = KRB5_TC_NOTICKET;
+    code = pkrb5_cc_set_flags(ctx, cache, flags);
+    if (code) {
+        if (code == KRB5_FCC_NOFILE || code == KRB5_CC_NOTFOUND) {
+            // Normal behavior; skip cache but suppress error message box
+            code = 0;
+        } else {
+            functionName = "krb5_cc_set_flags";
+        }
+        goto cleanup;
+    }
+    code = pkrb5_cc_get_principal(ctx, cache, &princ);
+    if (code) {
+        // Normal behavior; skip cache but suppress error message box
+        code = 0;
+        goto cleanup;
+    }
+    code = pkrb5_unparse_name(ctx, princ, &defname);
+    if (code) {
+        functionName = "krb5_unparse_name";
+        goto cleanup;
+    }
+    code = pkrb5_cc_start_seq_get(ctx, cache, &cur);
+    if (code) {
+        functionName = "krb5_cc_start_seq_get";
+        goto cleanup;
+    }
+    if (*ticketInfoTail)
+        ticketinfo = *ticketInfoTail;
+    else
+        // @fixme: calloc to init pointers
+        ticketinfo = (TICKETINFO *)calloc(1, sizeof(TICKETINFO));
+
+    if (ticketinfo == NULL) {
+        functionName = "calloc";
+        code = ENOMEM;
+        goto cleanup;
+    }
+    ticketinfo->next = NULL;
+    ticketinfo->ticket_list = NULL;
+    ticketinfo->principal = strdup(defname);
+    if (ticketinfo->principal == NULL) {
+        functionName = "strdup";
+        code = ENOMEM;
+        goto cleanup;
+    }
+    code = pkrb5_cc_get_full_name(ctx, cache, &ticketinfo->ccache_name);
+    if (code) {
+        functionName = "krb5_cc_get_full_name";
+        goto cleanup;
+    }
+    *ticketInfoTail = ticketinfo;
+    ticketListTail = &ticketinfo->ticket_list;
+    while (!(code = pkrb5_cc_next_cred(ctx, cache, &cur, &creds))) {
+        if (!pkrb5_is_config_principal(ctx, creds.server)) {
+            CredToTicketList(ctx, creds, defname, &ticketListTail);
+            CredToTicketInfo(creds, ticketinfo);
+        }
+        pkrb5_free_cred_contents(ctx, &creds);
+    }
+    if (code == KRB5_CC_END) {
+        code = pkrb5_cc_end_seq_get(ctx, cache, &cur);
+        if (code) {
+            functionName = "krb5_cc_end_seq_get";
+            goto cleanup;
+        }
+        flags = KRB5_TC_OPENCLOSE;      /* turns on OPENCLOSE mode */
+        code = pkrb5_cc_set_flags(ctx, cache, flags);
+        if (code) {
+            functionName = "krb5_cc_set_flags";
+            goto cleanup;
+        }
+    } else {
+        functionName = "krb5_cc_next_cred";
+        goto cleanup;
+    }
+cleanup:
+    if (code)
+        LeashKRB5Error(code, functionName);
+    if (ticketinfo) {
+        if (ticketinfo == *ticketInfoTail)
+            retval = 0;
+        else
+            LeashKRB5FreeTickets(&ticketinfo);
+    }
+    if (defname)
+        pkrb5_free_unparsed_name(ctx, defname);
+    if (princ)
+        pkrb5_free_principal(ctx, princ);
+    return retval;
+}
+
+
+//
+// Returns 0 for success, 1 for failure
+//
+int
+do_all_ccaches(krb5_context ctx, TICKETINFO **ticketinfotail)
+{
+    krb5_error_code code;
+    krb5_ccache cache;
+    krb5_cccol_cursor cursor;
+    int retval = 1;
+    char *functionName = NULL;
+
+    code = pkrb5_cccol_cursor_new(ctx, &cursor);
+    if (code) {
+        functionName = "krb5_cccol_cursor_new";
+        goto cleanup;
+    }
+    retval = 0;
+    while (!(code = pkrb5_cccol_cursor_next(ctx, cursor, &cache)) &&
+           cache != NULL) {
+        // Note that ticketinfotail will be updated here to point to the tail
+        // of the list but the caller of this function will remain with a
+        // pointer to the head.
+        if (do_ccache(ctx, cache, ticketinfotail) == 0)
+            ticketinfotail = &((*ticketinfotail)->next);
+        pkrb5_cc_close(ctx, cache);
+    }
+    if (code)
+         functionName = "krb5_cccol_cursor_next";
+    pkrb5_cccol_cursor_free(ctx, &cursor);
+cleanup:
+    if (code)
+        LeashKRB5Error(code, functionName);
+    return retval;
+}
+
+void
+LeashKRB5ListDefaultTickets(TICKETINFO *ticketinfo)
+{
+    krb5_error_code	code;
+    krb5_context ctx = 0;
+    krb5_ccache cache = 0;
+    char *functionName = NULL;
+
+    ticketinfo->btickets = NO_TICKETS;
+    ticketinfo->principal = NULL;
+    ticketinfo->ccache_name = NULL;
+    ticketinfo->next = NULL;
+    ticketinfo->ticket_list = NULL;
+    ticketinfo->renew_until = 0;
+    ticketinfo->valid_until = 0;
+    ticketinfo->issued = 0;
+
+    code = pkrb5_init_context(&ctx);
+    if (code) {
+        functionName = "krb5_init_context";
+        goto cleanup;
+    }
+
+    code = pkrb5_cc_default(ctx, &cache);
+    if (code) {
+        functionName = "krb5_cc_default";
+        goto cleanup;
+    }
+    if (cache != NULL)
+        do_ccache(ctx, cache, &ticketinfo);
+cleanup:
+    if (code)
+        LeashKRB5Error(code, functionName);
+    if (cache)
+        pkrb5_cc_close(ctx, cache);
+    if (ctx)
+        pkrb5_free_context(ctx);
+}
+
+
+/*
+ * LeashKRB5ListAllTickets()
+ */
+
+void
+LeashKRB5ListAllTickets(TICKETINFO **ticketinfo)
+{
+    krb5_error_code	code;
+    krb5_context ctx = 0;
+    char *functionName = NULL;
+
+    code = pkrb5_init_context(&ctx);
+    if (code) {
+        functionName = "krb5_init_context";
+        goto cleanup;
+    }
+
+    do_all_ccaches(ctx, ticketinfo);
+cleanup:
+    if (code)
+        LeashKRB5Error(code, functionName);
+    if (ctx)
+        pkrb5_free_context(ctx);
+}
diff --git a/src/windows/leash/Leash.cpp b/src/windows/leash/Leash.cpp
index 29e377a..44e687b 100644
--- a/src/windows/leash/Leash.cpp
+++ b/src/windows/leash/Leash.cpp
@@ -49,9 +49,6 @@ static char THIS_FILE[] = __FILE__;
 extern "C" int VScheckVersion(HWND hWnd, HANDLE hThisInstance);
 
 TicketInfoWrapper ticketinfo;
-#ifndef KRB5_TC_NOTICKET  /* test for krb5 1.4 and thread safety */
-HANDLE m_tgsReqMutex = 0;
-#endif
 
 HWND CLeashApp::m_hProgram = 0;
 HINSTANCE CLeashApp::m_hLeashDLL = 0;
@@ -69,6 +66,7 @@ krb5_context CLeashApp::m_krbv5_context = 0;
 profile_t CLeashApp::m_krbv5_profile = 0;
 HINSTANCE CLeashApp::m_hKrbLSA = 0;
 int CLeashApp::m_useRibbon = TRUE;
+BOOL CLeashApp::m_bUpdateDisplay = FALSE;
 
 /////////////////////////////////////////////////////////////////////////////
 // CLeashApp
@@ -95,9 +93,6 @@ CLeashApp::CLeashApp()
     memset(&ticketinfo, 0, sizeof(ticketinfo));
 
     ticketinfo.lockObj = CreateMutex(NULL, FALSE, NULL);
-#ifndef KRB5_TC_NOTICKET
-    m_tgsReqMutex = CreateMutex(NULL, FALSE, NULL);
-#endif
 
 #ifdef USE_HTMLHELP
 #if _MSC_VER >= 1300
@@ -121,9 +116,6 @@ CLeashApp::~CLeashApp()
 #ifdef COMMENT
 	/* Do not free the locking objects.  Doing so causes an invalid handle access */
     CloseHandle(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-    CloseHandle(m_tgsReqMutex);
-#endif
 #endif
 	AfxFreeLibrary(m_hLeashDLL);
 #ifndef NO_KRB4
@@ -170,6 +162,7 @@ BOOL CLeashApp::InitInstance()
     // NOTE: Not used at this time
     /// Set LEASH_DLL to the path where the Leash.exe is
     char modulePath[MAX_PATH];
+    krb5_error_code code;
     DWORD result = GetModuleFileName(AfxGetInstanceHandle(), modulePath, MAX_PATH);
     ASSERT(result);
 
@@ -189,6 +182,11 @@ BOOL CLeashApp::InitInstance()
     HWND hMsg = GetForegroundWindow();
     if (!InitDLLs())
         return FALSE; //exit program, can't load LEASHDLL
+    code = pkrb5_init_context(&m_krbv5_context);
+    if (code) {
+        // @TODO: report error
+        return FALSE;
+    }
 
     // Check for args (switches)
     LPCTSTR exeFile		= __targv[0];
@@ -207,17 +205,12 @@ BOOL CLeashApp::InitInstance()
 		char username[64]="";
 		char realm[192]="";
 		int i=0, j=0;
-                TicketList* ticketList = NULL;
                 if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
                     throw("Unable to lock ticketinfo");
 
-                pLeashKRB5GetTickets(&ticketinfo.Krb5, &ticketList,
-                                      &CLeashApp::m_krbv5_context);
-                pLeashFreeTicketList(&ticketList);
-                pLeashKRB4GetTickets(&ticketinfo.Krb4, &ticketList);
-                pLeashFreeTicketList(&ticketList);
+                LeashKRB5ListDefaultTickets(&ticketinfo.Krb5);
 
-                if ( ticketinfo.Krb5.btickets && ticketinfo.Krb5.principal[0] ) {
+                if ( ticketinfo.Krb5.btickets && ticketinfo.Krb5.principal ) {
                     for (; ticketinfo.Krb5.principal[i] && ticketinfo.Krb5.principal[i] != '@'; i++)
                     {
                         username[i] = ticketinfo.Krb5.principal[i];
@@ -230,20 +223,10 @@ BOOL CLeashApp::InitInstance()
                         }
                     }
                     realm[j] = '\0';
-                } else if ( ticketinfo.Krb4.btickets && ticketinfo.Krb4.principal[0] ) {
-                    for (; ticketinfo.Krb4.principal[i] && ticketinfo.Krb4.principal[i] != '@'; i++)
-                    {
-                        username[i] = ticketinfo.Krb4.principal[i];
-                    }
-                    username[i] = '\0';
-                    if (ticketinfo.Krb4.principal[i]) {
-                        for (i++ ; ticketinfo.Krb4.principal[i] ; i++, j++)
-                        {
-                            realm[j] = ticketinfo.Krb4.principal[i];
-                        }
-                    }
-                    realm[j] = '\0';
                 }
+
+                LeashKRB5FreeTicketInfo(&ticketinfo.Krb5);
+
                 ReleaseMutex(ticketinfo.lockObj);
 
 				ldi.size = LSH_DLGINFO_EX_V1_SZ;
@@ -415,14 +398,11 @@ BOOL CLeashApp::InitInstance()
     // Check to see if there are any tickets in the cache
     // If not and the Windows Logon Session is Kerberos authenticated attempt an import
     {
-        TicketList* ticketList = NULL;
         if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
             throw("Unable to lock ticketinfo");
-        pLeashKRB5GetTickets(&ticketinfo.Krb5, &ticketList, &CLeashApp::m_krbv5_context);
-        pLeashFreeTicketList(&ticketList);
-        pLeashKRB4GetTickets(&ticketinfo.Krb4, &ticketList);
-        pLeashFreeTicketList(&ticketList);
-        BOOL b_autoinit = !ticketinfo.Krb4.btickets && !ticketinfo.Krb5.btickets;
+        LeashKRB5ListDefaultTickets(&ticketinfo.Krb5);
+        BOOL b_autoinit = !ticketinfo.Krb5.btickets;
+        LeashKRB5FreeTicketInfo(&ticketinfo.Krb5);
         ReleaseMutex(ticketinfo.lockObj);
 
         DWORD dwMsLsaImport = pLeash_get_default_mslsa_import();
@@ -503,9 +483,7 @@ BOOL CLeashApp::InitInstance()
 
 // leash functions
 DECL_FUNC_PTR(not_an_API_LeashKRB4GetTickets);
-DECL_FUNC_PTR(not_an_API_LeashKRB5GetTickets);
 DECL_FUNC_PTR(not_an_API_LeashAFSGetToken);
-DECL_FUNC_PTR(not_an_API_LeashFreeTicketList);
 DECL_FUNC_PTR(not_an_API_LeashGetTimeServerName);
 DECL_FUNC_PTR(Leash_kdestroy);
 DECL_FUNC_PTR(Leash_changepwd_dlg);
@@ -552,9 +530,7 @@ DECL_FUNC_PTR(Leash_reset_defaults);
 
 FUNC_INFO leash_fi[] = {
     MAKE_FUNC_INFO(not_an_API_LeashKRB4GetTickets),
-    MAKE_FUNC_INFO(not_an_API_LeashKRB5GetTickets),
     MAKE_FUNC_INFO(not_an_API_LeashAFSGetToken),
-    MAKE_FUNC_INFO(not_an_API_LeashFreeTicketList),
     MAKE_FUNC_INFO(not_an_API_LeashGetTimeServerName),
     MAKE_FUNC_INFO(Leash_kdestroy),
     MAKE_FUNC_INFO(Leash_changepwd_dlg),
@@ -795,7 +771,7 @@ BOOL CLeashApp::InitDLLs()
 #endif
     m_hKrb5DLL = AfxLoadLibrary(KERB5DLL);
     m_hKrb5ProfileDLL = AfxLoadLibrary(KERB5_PPROFILE_DLL);
-    m_hComErr - AfxLoadLibrary(COMERR_DLL);
+    m_hComErr = AfxLoadLibrary(COMERR_DLL);
 
 #ifndef NO_AFS
     afscompat_init();
@@ -1504,31 +1480,19 @@ CLeashApp::ProbeKDC(void)
 VOID
 CLeashApp::ObtainTicketsViaUserIfNeeded(HWND hWnd)
 {
-    TicketList* ticketList = NULL;
     if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
         throw("Unable to lock ticketinfo");
-    pLeashKRB5GetTickets(&ticketinfo.Krb5, &ticketList, &CLeashApp::m_krbv5_context);
-    pLeashFreeTicketList(&ticketList);
-    pLeashKRB4GetTickets(&ticketinfo.Krb4, &ticketList);
-    pLeashFreeTicketList(&ticketList);
+    LeashKRB5ListDefaultTickets(&ticketinfo.Krb5);
+    int btickets = ticketinfo.Krb5.btickets;
+    LeashKRB5FreeTicketInfo(&ticketinfo.Krb5);
+    ReleaseMutex(ticketinfo.lockObj);
 
-    if ( !ticketinfo.Krb4.btickets && !ticketinfo.Krb5.btickets ) {
-        ReleaseMutex(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-        if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-            throw("Unable to lock TGS mutex");
-#endif
+    if ( !btickets ) {
         if ( pLeash_importable() ) {
             if (pLeash_import())
                 CLeashView::m_importedTickets = 1;
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
         }
         else if ( ProbeKDC() ) {
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
             LSH_DLGINFO_EX ldi;
             ldi.size = LSH_DLGINFO_EX_V1_SZ;
             ldi.dlgtype = DLGTYPE_PASSWD;
@@ -1540,54 +1504,12 @@ CLeashApp::ObtainTicketsViaUserIfNeeded(HWND hWnd)
 
             pLeash_kinit_dlg_ex(hWnd, &ldi);
         }
-#ifndef KRB5_TC_NOTICKET
-        else {
-            ReleaseMutex(m_tgsReqMutex);
-        }
-#endif
-    } else if ( ticketinfo.Krb5.btickets ) {
-        ReleaseMutex(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-        if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-            throw("Unable to TGS mutex");
-#endif
+    } else {
         if ( CLeashView::m_importedTickets && pLeash_importable() ) {
             if (pLeash_import())
                 CLeashView::m_importedTickets = 1;
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
         }
         else if ( ProbeKDC() && !pLeash_renew() ) {
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
-            LSH_DLGINFO_EX ldi;
-            ldi.size = LSH_DLGINFO_EX_V1_SZ;
-            ldi.dlgtype = DLGTYPE_PASSWD;
-            ldi.title = "Get Ticket";
-            ldi.username = NULL;
-            ldi.realm = NULL;
-            ldi.dlgtype = DLGTYPE_PASSWD;
-            ldi.use_defaults = 1;
-
-            pLeash_kinit_dlg_ex(hWnd, &ldi);
-        }
-#ifndef KRB5_TC_NOTICKET
-        else {
-            ReleaseMutex(m_tgsReqMutex);
-        }
-#endif
-    } else if ( ticketinfo.Krb4.btickets ) {
-        ReleaseMutex(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-        if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-            throw("Unable to lock TGS mutex");
-#endif
-        if ( ProbeKDC() ) {
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
             LSH_DLGINFO_EX ldi;
             ldi.size = LSH_DLGINFO_EX_V1_SZ;
             ldi.dlgtype = DLGTYPE_PASSWD;
@@ -1599,14 +1521,6 @@ CLeashApp::ObtainTicketsViaUserIfNeeded(HWND hWnd)
 
             pLeash_kinit_dlg_ex(hWnd, &ldi);
         }
-#ifndef KRB5_TC_NOTICKET
-        else {
-            ReleaseMutex(m_tgsReqMutex);
-        }
-#endif
-    } else {
-        ReleaseMutex(ticketinfo.lockObj);
-        // Do nothing ...
     }
     return;
 }
@@ -1681,10 +1595,6 @@ CLeashApp::IpAddrChangeMonitorInit(HWND hWnd)
 UINT
 CLeashApp::InitWorker(void * hWnd)
 {
-#ifndef KRB5_TC_NOTICKET
-    if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock tgsReq");
-#endif
     if ( ProbeKDC() ) {
         LSH_DLGINFO_EX ldi;
         ldi.size = LSH_DLGINFO_EX_V1_SZ;
@@ -1694,16 +1604,9 @@ CLeashApp::InitWorker(void * hWnd)
         ldi.realm = NULL;
         ldi.use_defaults = 1;
 
-#ifndef KRB5_TC_NOTICKET
-        ReleaseMutex(m_tgsReqMutex);
-#endif
         pLeash_kinit_dlg_ex((HWND)hWnd, &ldi);
         ::SendMessage((HWND)hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
     }
-#ifndef KRB5_TC_NOTICKET
-    else
-        ReleaseMutex(m_tgsReqMutex);
-#endif
     return 0;
 }
 
@@ -1724,3 +1627,15 @@ CLeashApp::WinHelp(DWORD dwData, UINT nCmd)
 }
 #endif
 #endif
+
+
+BOOL CLeashApp::OnIdle(LONG lCount)
+{
+    // TODO: Add your specialized code here and/or call the base class
+    BOOL retval = CWinAppEx::OnIdle(lCount);
+    if ((lCount == 0) && m_bUpdateDisplay) {
+        m_bUpdateDisplay = FALSE;
+        m_pMainWnd->SendMessage(WM_COMMAND, ID_UPDATE_DISPLAY, 0);
+    }
+    return retval;
+}
diff --git a/src/windows/leash/Leash.h b/src/windows/leash/Leash.h
index 377f0fc..6d5f815 100644
--- a/src/windows/leash/Leash.h
+++ b/src/windows/leash/Leash.h
@@ -120,6 +120,7 @@ public:
 	static profile_t    m_krbv5_profile;
 	static HINSTANCE    m_hKrbLSA;
 	static int          m_useRibbon; // temporary while ribbon UI in dev
+	static BOOL         m_bUpdateDisplay;
 
 	CLeashApp();
 	virtual ~CLeashApp();
@@ -155,6 +156,8 @@ public:
 	//{{AFX_MSG(CLeashApp)
     //}}AFX_MSG
 	DECLARE_MESSAGE_MAP()
+public:
+    virtual BOOL OnIdle(LONG lCount);
 };
 
 extern CLeashApp theApp;
diff --git a/src/windows/leash/Leash.rc b/src/windows/leash/Leash.rc
index f4100b7..23da2cc 100644
--- a/src/windows/leash/Leash.rc
+++ b/src/windows/leash/Leash.rc
@@ -689,6 +689,10 @@ BEGIN
         BOTTOMMARGIN, 190
     END
 
+    IDD_LEASH_MESSAGE_BOX, DIALOG
+    BEGIN
+    END
+
     IDD_KRB4_PROP_LOCATION, DIALOG
     BEGIN
         LEFTMARGIN, 6
diff --git a/src/windows/leash/LeashView.cpp b/src/windows/leash/LeashView.cpp
index 6a28727..c139ff7 100644
--- a/src/windows/leash/LeashView.cpp
+++ b/src/windows/leash/LeashView.cpp
@@ -55,6 +55,7 @@ BEGIN_MESSAGE_MAP(CLeashView, CListView)
     ON_COMMAND(ID_IMPORT_TICKET, OnImportTicket)
 	ON_COMMAND(ID_DESTROY_TICKET, OnDestroyTicket)
 	ON_COMMAND(ID_CHANGE_PASSWORD, OnChangePassword)
+	ON_COMMAND(ID_MAKE_DEFAULT, OnMakeDefault)
 	ON_COMMAND(ID_UPDATE_DISPLAY, OnUpdateDisplay)
 	ON_COMMAND(ID_SYN_TIME, OnSynTime)
 	ON_COMMAND(ID_DEBUG_MODE, OnDebugMode)
@@ -94,6 +95,7 @@ BEGIN_MESSAGE_MAP(CLeashView, CListView)
 	ON_UPDATE_COMMAND_UI(ID_KRB4_PROPERTIES, OnUpdateKrb4Properties)
 	ON_UPDATE_COMMAND_UI(ID_KRB5_PROPERTIES, OnUpdateKrb5Properties)
 	ON_UPDATE_COMMAND_UI(ID_AFS_CONTROL_PANEL, OnUpdateAfsControlPanel)
+	ON_UPDATE_COMMAND_UI(ID_MAKE_DEFAULT, OnUpdateMakeDefault)
 	ON_COMMAND(ID_PROPERTIES, OnKrbProperties)
 	ON_UPDATE_COMMAND_UI(ID_PROPERTIES, OnUpdateProperties)
 	ON_COMMAND(ID_HELP_KERBEROS_, OnHelpKerberos)
@@ -109,6 +111,10 @@ BEGIN_MESSAGE_MAP(CLeashView, CListView)
     ON_NOTIFY(HDN_ITEMCHANGED, 0, OnItemChanged)
 	//}}AFX_MSG_MAP
 
+    ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CLeashView::OnLvnItemchanging)
+    ON_NOTIFY_REFLECT(LVN_ITEMACTIVATE, &CLeashView::OnLvnItemActivate)
+    ON_NOTIFY_REFLECT(LVN_KEYDOWN, &CLeashView::OnLvnKeydown)
+    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CLeashView::OnNMCustomdraw)
 END_MESSAGE_MAP()
 
 
@@ -145,10 +151,149 @@ ViewColumnInfo CLeashView::sm_viewColumns[] =
 };
 
 
+static HFONT CreateBoldFont(HFONT font)
+{
+    // @TODO: Should probably enumerate fonts here instead since this
+    // does not actually seem to guarantee returning a new font
+    // distinguishable from the original.
+    LOGFONT fontAttributes = { 0 };
+    ::GetObject(font, sizeof(fontAttributes), &fontAttributes);
+    fontAttributes.lfWeight = FW_BOLD;
+    HFONT boldFont = ::CreateFontIndirect(&fontAttributes);
+    return boldFont;
+}
+
+static HFONT CreateItalicFont(HFONT font)
+{
+    LOGFONT fontAttributes = { 0 };
+    ::GetObject(font, sizeof(fontAttributes), &fontAttributes);
+    fontAttributes.lfItalic = TRUE;
+    HFONT boldFont = ::CreateFontIndirect(&fontAttributes);
+    return boldFont;
+}
+
+
 bool change_icon_size = true;
-#ifndef KRB5_TC_NOTICKET
-extern HANDLE m_tgsReqMutex;
-#endif
+
+void krb5TimestampToFileTime(krb5_timestamp t, LPFILETIME pft)
+{
+    // Note that LONGLONG is a 64-bit value
+    LONGLONG ll;
+
+    ll = Int32x32To64(t, 10000000) + 116444736000000000;
+    pft->dwLowDateTime = (DWORD)ll;
+    pft->dwHighDateTime = ll >> 32;
+}
+
+// allocate outstr
+void krb5TimestampToLocalizedString(krb5_timestamp t, LPTSTR *outStr)
+{
+    FILETIME ft, lft;
+    SYSTEMTIME st;
+    krb5TimestampToFileTime(t, &ft);
+    FileTimeToLocalFileTime(&ft, &lft);
+    FileTimeToSystemTime(&lft, &st);
+    TCHAR timeFormat[80]; // 80 is max required for LOCALE_STIMEFORMAT
+    GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
+                  LOCALE_STIMEFORMAT,
+                  timeFormat,
+                  sizeof(timeFormat) / sizeof(timeFormat[0]));
+
+    int timeSize = GetTimeFormat(LOCALE_SYSTEM_DEFAULT,
+                                 TIME_NOSECONDS,
+                                 &st,
+                                 timeFormat,
+                                 NULL,
+                                 0);
+    // Using dateFormat prevents localization of Month/day order,
+    // but there is no other way AFAICT to suppress the year
+    TCHAR * dateFormat = "MMM dd'  '";
+    int dateSize = GetDateFormat(LOCALE_SYSTEM_DEFAULT,
+        0, // flags
+        &st,
+        dateFormat, // format
+        NULL, // date string
+        0);
+
+    if (*outStr)
+        free(*outStr);
+
+    // Allocate string for combined date and time,
+    // but only need one terminating NULL
+    LPTSTR str = (LPSTR)malloc((dateSize + timeSize - 1) * sizeof(TCHAR));
+    if (!str) {
+        // LeashWarn allocation failure
+        *outStr = NULL;
+        return;
+    }
+    GetDateFormat(LOCALE_SYSTEM_DEFAULT,
+        0, // flags
+        &st,
+        dateFormat, // format
+        &str[0],
+        dateSize);
+
+    GetTimeFormat(LOCALE_SYSTEM_DEFAULT,
+                    TIME_NOSECONDS,
+                    &st,
+                    timeFormat,
+                    &str[dateSize - 1],
+                    timeSize);
+    *outStr = str;
+}
+
+#define SECONDS_PER_MINUTE (60)
+#define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE)
+#define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR)
+#define MAX_DURATION_STR 255
+// convert time in seconds to string
+void DurationToString(long delta, LPTSTR *outStr)
+{
+    int days;
+    int hours;
+    int minutes;
+    TCHAR minutesStr[MAX_DURATION_STR+1];
+    TCHAR hoursStr[MAX_DURATION_STR+1];
+
+    if (*outStr)
+        free(*outStr);
+    *outStr = (LPSTR)malloc((MAX_DURATION_STR + 1)* sizeof(TCHAR));
+    if (!(*outStr))
+        return;
+
+    days = delta / SECONDS_PER_DAY;
+    delta -= days * SECONDS_PER_DAY;
+    hours = delta / SECONDS_PER_HOUR;
+    delta -= hours * SECONDS_PER_HOUR;
+    minutes = delta / SECONDS_PER_MINUTE;
+
+    if (minutes != 1)
+        _snprintf(minutesStr, MAX_DURATION_STR, "%d minutes", minutes);
+    else
+        _snprintf(minutesStr, MAX_DURATION_STR, "1 minute");
+    minutesStr[MAX_DURATION_STR] = 0;
+
+    if (hours != 1)
+        _snprintf(hoursStr, MAX_DURATION_STR, "%d hours", hours);
+    else
+        _snprintf(hoursStr, MAX_DURATION_STR, "1 hour");
+    hoursStr[MAX_DURATION_STR] = 0;
+
+    if (days > 0) {
+        if (days > 1)
+            _snprintf(*outStr, MAX_DURATION_STR, "(%d days, %s remaining)",
+                      days, hoursStr);
+        else
+            _snprintf(*outStr, MAX_DURATION_STR, "(1 day, %s remaining)",
+                      hoursStr);
+    } else if (hours > 0) {
+        _snprintf(*outStr, MAX_DURATION_STR, "(%s, %s remaining)", hoursStr,
+                  minutesStr);
+    } else {
+        _snprintf(*outStr, MAX_DURATION_STR, "(%s remaining)", minutesStr);
+    }
+    (*outStr)[MAX_DURATION_STR] = 0;
+}
 
 /////////////////////////////////////////////////////////////////////////////
 // CLeashView construction/destruction
@@ -159,7 +304,6 @@ CLeashView::CLeashView()
 #ifndef NO_KRB4
     m_listKrb4 = NULL;
 #endif
-    m_listKrb5 = NULL;
     m_listAfs = NULL;
     m_startup = TRUE;
     m_warningOfTicketTimeLeftKrb4 = 0;
@@ -188,7 +332,7 @@ CLeashView::CLeashView()
     ResetTreeNodes();
     m_hMenu = NULL;
     m_pApp = NULL;
-    m_pImageList = NULL;
+    m_ccacheDisplay = NULL;
     m_forwardableTicket = 0;
     m_proxiableTicket = 0;
     m_renewableTicket = 0;
@@ -199,20 +343,31 @@ CLeashView::CLeashView()
     m_pWarningMessage = NULL;
     m_bIconAdded = FALSE;
     m_bIconDeleted = FALSE;
-#ifndef KRB5_TC_NOTICKET
-    m_tgsReqMutex = CreateMutex(NULL, FALSE, NULL);
-#endif
+    m_BaseFont = NULL;
+    m_BoldFont = NULL;
+    m_ItalicFont = NULL;
+    m_aListItemInfo = NULL;
 }
 
 
 CLeashView::~CLeashView()
 {
-#ifndef KRB5_TC_NOTICKET
-    CloseHandle(m_tgsReqMutex);
-#endif
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    while (elem) {
+        CCacheDisplayData *next = elem->m_next;
+        delete elem;
+        elem = next;
+    }
+    m_ccacheDisplay = NULL;
     // destroys window if not already destroyed
     if (m_pDebugWindow)
         delete m_pDebugWindow;
+    if (m_BoldFont)
+        DeleteObject(m_BoldFont);
+    if (m_ItalicFont)
+        DeleteObject(m_ItalicFont);
+    if (m_aListItemInfo)
+        delete[] m_aListItemInfo;
 }
 
 void CLeashView::OnItemChanged(NMHDR* pNmHdr, LRESULT* pResult)
@@ -298,8 +453,7 @@ time_t CLeashView::LeashTime()
 // Call while possessing a lock to ticketinfo.lockObj
 INT CLeashView::GetLowTicketStatus(int ver)
 {
-    BOOL b_notix = (ver == 4 && !ticketinfo.Krb4.btickets) ||
-                   (ver == 5 && !ticketinfo.Krb5.btickets) ||
+    BOOL b_notix = (ver == 5 && !ticketinfo.Krb5.btickets) ||
                    (ver == 1 && !ticketinfo.Afs.btickets);
 
     if (b_notix)
@@ -317,14 +471,12 @@ INT CLeashView::GetLowTicketStatus(int ver)
 
 VOID CLeashView::UpdateTicketTime(TICKETINFO& ti)
 {
-    if (!ti.btickets)
-    {
+    if (!ti.btickets) {
         m_ticketTimeLeft = 0L;
         return;
     }
 
-    m_ticketTimeLeft = ti.issue_date + ti.lifetime -
-        LeashTime();
+    m_ticketTimeLeft = ti.valid_until - LeashTime();
 
     if (m_ticketTimeLeft <= 0L)
         ti.btickets = EXPIRED_TICKETS;
@@ -443,10 +595,6 @@ VOID CLeashView::OnInitTicket()
 
 UINT CLeashView::InitTicket(void * hWnd)
 {
-#ifndef KRB5_TC_NOTICKET
-    if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock TGS request mutex");
-#endif
     m_importedTickets = 0;
 
     LSH_DLGINFO_EX ldi;
@@ -454,33 +602,28 @@ UINT CLeashView::InitTicket(void * hWnd)
     char realm[192];
     int i=0, j=0;
     if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
-#ifndef KRB5_TC_NOTICKET
-        ReleaseMutex(m_tgsReqMutex);
-#endif
         throw("Unable to lock ticketinfo");
     }
-
+    LeashKRB5ListDefaultTickets(&ticketinfo.Krb5);
     char * principal = ticketinfo.Krb5.principal;
-    if (!*principal)
-        principal = ticketinfo.Krb4.principal;
-    for (; principal[i] && principal[i] != '@'; i++)
-    {
-        username[i] = principal[i];
-    }
+    if (principal)
+        for (; principal[i] && principal[i] != '@'; i++)
+            username[i] = principal[i];
     username[i] = '\0';
-    if (principal[i]) {
+    if (principal && principal[i]) {
         for (i++ ; principal[i] ; i++, j++)
         {
             realm[j] = principal[i];
         }
     }
     realm[j] = '\0';
+    LeashKRB5FreeTicketInfo(&ticketinfo.Krb5);
     ReleaseMutex(ticketinfo.lockObj);
 
     ldi.size = sizeof(ldi);
     ldi.dlgtype = DLGTYPE_PASSWD;
     ldi.title = ldi.in.title;
-    strcpy(ldi.in.title,"Initialize Ticket");
+    strcpy(ldi.in.title,"Get Ticket");
     ldi.username = ldi.in.username;
     strcpy(ldi.in.username,username);
     ldi.realm = ldi.in.realm;
@@ -492,15 +635,9 @@ UINT CLeashView::InitTicket(void * hWnd)
     {
         AfxMessageBox("There is a problem finding the Leash Window!",
                    MB_OK|MB_ICONSTOP);
-#ifndef KRB5_TC_NOTICKET
-        ReleaseMutex(m_tgsReqMutex);
-#endif
         return 0;
     }
 
-#ifndef KRB5_TC_NOTICKET
-    ReleaseMutex(m_tgsReqMutex);
-#endif
     int result = pLeash_kinit_dlg_ex((HWND)hWnd, &ldi);
 
     if (-1 == result)
@@ -510,26 +647,15 @@ UINT CLeashView::InitTicket(void * hWnd)
     }
     else if ( result )
     {
-#ifndef KRB5_TC_NOTICKET
-        if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-            throw("Unable to lock TGS request mutex");
-#endif
         if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
             throw("Unable to lock ticketinfo");
         }
-        ticketinfo.Krb4.btickets = GOOD_TICKETS;
         m_warningOfTicketTimeLeftKrb4 = 0;
         m_warningOfTicketTimeLeftKrb5 = 0;
         m_ticketStatusKrb4 = 0;
         m_ticketStatusKrb5 = 0;
         ReleaseMutex(ticketinfo.lockObj);
         m_autoRenewalAttempted = 0;
-#ifndef KRB5_TC_NOTICKET
-        ReleaseMutex(m_tgsReqMutex);
-#endif
         ::SendMessage((HWND)hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
     }
     return 0;
@@ -550,19 +676,10 @@ UINT CLeashView::ImportTicket(void * hWnd)
     if ( !CLeashApp::m_hKrb5DLL )
         return 0;
 
-#ifndef KRB5_TC_NOTICKET
-    if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock TGS request mutex");
-#endif
-    int import = 0;
-    int warning = 0;
-
     krb5_error_code code;
     krb5_ccache mslsa_ccache=0;
     krb5_principal princ = 0;
     char * pname = 0;
-    LONG krb5Error = 0;
-    TicketList * tlist = NULL;
 
     if (code = pkrb5_cc_resolve(CLeashApp::m_krbv5_context, "MSLSA:", &mslsa_ccache))
         goto cleanup;
@@ -573,21 +690,7 @@ UINT CLeashView::ImportTicket(void * hWnd)
     if (code = pkrb5_unparse_name(CLeashApp::m_krbv5_context, princ, &pname))
         goto cleanup;
 
-    if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
-#ifndef KRB5_TC_NOTICKET
-        ReleaseMutex(m_tgsReqMutex);
-#endif
-        throw("Unable to lock ticketinfo");
-    }
-    krb5Error = pLeashKRB5GetTickets( &ticketinfo.Krb5, &tlist,
-                                      &CLeashApp::m_krbv5_context);
-    if ( tlist )
-        pLeashFreeTicketList(&tlist);
-
-    warning = strcmp(ticketinfo.Krb5.principal, pname) && ticketinfo.Krb5.btickets;
-    ReleaseMutex(ticketinfo.lockObj);
-
-  cleanup:
+cleanup:
     if (pname)
         pkrb5_free_unparsed_name(CLeashApp::m_krbv5_context, pname);
 
@@ -598,105 +701,41 @@ UINT CLeashView::ImportTicket(void * hWnd)
         pkrb5_cc_close(CLeashApp::m_krbv5_context, mslsa_ccache);
 
     if ( code == 0 ) {
-        if (warning)
+        int result = pLeash_import();
+        if (-1 == result)
         {
-            INT whatToDo;
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
-            if (!CLeashApp::m_hAfsDLL
-////@#+Need to rework this logic. I am confused what !m_hKrb4DLL means in this case!
-#ifndef NO_KRB4
-		|| !CLeashApp::m_hKrb4DLL
-#endif
-		)
-                whatToDo = AfxMessageBox("You are about to replace your existing ticket(s)\n"
-                                          "with a ticket imported from the Windows credential cache!",
-                                          MB_OKCANCEL, 0);
-            else
-                whatToDo = AfxMessageBox("You are about to replace your existing ticket(s)/token(s)"
-                                          "with ticket imported from the Windows credential cache!",
-                                          MB_OKCANCEL, 0);
-#ifndef KRB5_TC_NOTICKET
-            if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-                throw("Unable to lock tgsReqMutex");
-#endif
-            if (whatToDo == IDOK)
-            {
-                pLeash_kdestroy();
-                import = 1;
-            }
-        } else {
-            import = 1;
+            AfxMessageBox("There is a problem importing tickets!",
+                            MB_OK|MB_ICONSTOP);
+            ::SendMessage((HWND)hWnd,WM_COMMAND, ID_UPDATE_DISPLAY, 0);
+            m_importedTickets = 0;
         }
-
-        if ( import ) {
-            int result = pLeash_import();
-            if (-1 == result)
-            {
-#ifndef KRB5_TC_NOTICKET
-                ReleaseMutex(m_tgsReqMutex);
-#endif
-                AfxMessageBox("There is a problem importing tickets!",
-                               MB_OK|MB_ICONSTOP);
-                ::SendMessage((HWND)hWnd,WM_COMMAND, ID_UPDATE_DISPLAY, 0);
-                m_importedTickets = 0;
+        else
+        {
+            if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
+                throw("Unable to lock ticketinfo");
             }
-            else
-            {
-                if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
-#ifndef KRB5_TC_NOTICKET
-                    ReleaseMutex(m_tgsReqMutex);
-#endif
-                    throw("Unable to lock ticketinfo");
-                }
-                ticketinfo.Krb4.btickets = GOOD_TICKETS;
-                ticketinfo.Krb5.btickets = GOOD_TICKETS;
-                m_warningOfTicketTimeLeftKrb4 = 0;
-                m_warningOfTicketTimeLeftKrb5 = 0;
-                m_ticketStatusKrb4 = 0;
-                m_ticketStatusKrb5 = 0;
-                ReleaseMutex(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-                ReleaseMutex(m_tgsReqMutex);
-#endif
-                ::SendMessage((HWND)hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
+            ticketinfo.Krb5.btickets = GOOD_TICKETS;
+            m_warningOfTicketTimeLeftKrb4 = 0;
+            m_warningOfTicketTimeLeftKrb5 = 0;
+            m_ticketStatusKrb4 = 0;
+            m_ticketStatusKrb5 = 0;
+            ReleaseMutex(ticketinfo.lockObj);
+            ::SendMessage((HWND)hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
 
-#ifndef KRB5_TC_NOTICKET
-                if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-                    throw("Unable to lock tgsReqMutex");
-#endif
-                if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
-#ifndef KRB5_TC_NOTICKET
-                    ReleaseMutex(m_tgsReqMutex);
-#endif
-                    throw("Unable to lock ticketinfo");
-                }
-#ifndef KRB5_TC_NOTICKET
-                ReleaseMutex(m_tgsReqMutex);
-#endif
+            if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
+                throw("Unable to lock ticketinfo");
+            }
 
-                if (ticketinfo.Krb5.btickets != GOOD_TICKETS) {
-                    ReleaseMutex(ticketinfo.lockObj);
-                    AfxBeginThread(InitTicket,hWnd);
-                } else {
-                    ReleaseMutex(ticketinfo.lockObj);
-                    m_importedTickets = 1;
-                    m_autoRenewalAttempted = 0;
-                }
+            if (ticketinfo.Krb5.btickets != GOOD_TICKETS) {
+                ReleaseMutex(ticketinfo.lockObj);
+                AfxBeginThread(InitTicket,hWnd);
+            } else {
+                ReleaseMutex(ticketinfo.lockObj);
+                m_importedTickets = 1;
+                m_autoRenewalAttempted = 0;
             }
         }
-#ifndef KRB5_TC_NOTICKET
-        else {
-            ReleaseMutex(m_tgsReqMutex);
-        }
-#endif
-    }
-#ifndef KRB5_TC_NOTICKET
-    else {
-        ReleaseMutex(m_tgsReqMutex);
     }
-#endif
     return 0;
 }
 
@@ -718,38 +757,17 @@ UINT CLeashView::RenewTicket(void * hWnd)
     if ( !CLeashApp::m_hKrb5DLL )
         return 0;
 
-#ifndef KRB5_TC_NOTICKET
-    if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock TGS request mutex");
-#endif
-
     // Try to renew
     BOOL b_renewed = pLeash_renew();
-    TicketList * tlist = NULL;
-    if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0) {
-#ifndef KRB5_TC_NOTICKET
-        ReleaseMutex(m_tgsReqMutex);
-#endif
-        throw("Unable to lock ticketinfo");
-    }
-    LONG krb5Error = pLeashKRB5GetTickets(&ticketinfo.Krb5, &tlist,
-                                           &CLeashApp::m_krbv5_context);
-    pLeashFreeTicketList(&tlist);
     if ( b_renewed ) {
-        if (!krb5Error && ticketinfo.Krb5.btickets == GOOD_TICKETS) {
-            ticketinfo.Krb4.btickets = GOOD_TICKETS;
-            m_warningOfTicketTimeLeftKrb4 = 0;
-            m_warningOfTicketTimeLeftKrb5 = 0;
-            m_ticketStatusKrb4 = 0;
-            m_ticketStatusKrb5 = 0;
-            m_autoRenewalAttempted = 0;
-            ReleaseMutex(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
-            ::SendMessage((HWND)hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
-            return 0;
-        }
+        m_warningOfTicketTimeLeftKrb4 = 0;
+        m_warningOfTicketTimeLeftKrb5 = 0;
+        m_ticketStatusKrb4 = 0;
+        m_ticketStatusKrb5 = 0;
+        m_autoRenewalAttempted = 0;
+        ReleaseMutex(ticketinfo.lockObj);
+        ::SendMessage((HWND)hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
+        return 0;
     }
 
     krb5_error_code code;
@@ -770,8 +788,6 @@ UINT CLeashView::RenewTicket(void * hWnd)
         m_importedTickets = 1;
 
   cleanup:
-    ReleaseMutex(ticketinfo.lockObj);
-
     if (pname)
         pkrb5_free_unparsed_name(CLeashApp::m_krbv5_context, pname);
 
@@ -781,9 +797,6 @@ UINT CLeashView::RenewTicket(void * hWnd)
     if (mslsa_ccache)
         pkrb5_cc_close(CLeashApp::m_krbv5_context, mslsa_ccache);
 
-#ifndef KRB5_TC_NOTICKET
-    ReleaseMutex(m_tgsReqMutex);
-#endif
     // If imported from Kerberos LSA, re-import
     // Otherwise, init the tickets
     if ( m_importedTickets )
@@ -794,28 +807,61 @@ UINT CLeashView::RenewTicket(void * hWnd)
     return 0;
 }
 
+static void kdestroy(const char *ccache_name)
+{
+    krb5_context ctx;
+    krb5_ccache ccache=NULL;
+    int code = pkrb5_init_context(&ctx);
+    if (code) {
+        // TODO: spew error
+        goto cleanup;
+    }
+    code = pkrb5_cc_resolve(ctx, ccache_name, &ccache);
+    if (code) {
+        // TODO: spew error
+        goto cleanup;
+    }
+    code = pkrb5_cc_destroy(ctx, ccache);
+    if (code) {
+        goto cleanup;
+    }
+cleanup:
+    if (ctx)
+        pkrb5_free_context(ctx);
+}
+
+
 VOID CLeashView::OnDestroyTicket()
 {
-    if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock ticketinfo");
-    BOOL b_destroy =ticketinfo.Krb4.btickets || ticketinfo.Krb5.btickets || ticketinfo.Afs.btickets;
-    ReleaseMutex(ticketinfo.lockObj);
+    // @TODO: grab mutex
+    BOOL destroy = FALSE;
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    while (elem) {
+        if (elem->m_selected) {
+            // @TODO add princ to msg text
+            destroy = TRUE;
+        }
+        elem = elem->m_next;
+    }
+    // release mutex
 
-    if (b_destroy)
+    if (destroy)
     {
         INT whatToDo;
 
-        if (!CLeashApp::m_hAfsDLL)
-            whatToDo = AfxMessageBox("Are you sure you want to destroy these tickets?",
-                                     MB_ICONEXCLAMATION|MB_YESNO, 0);
-        else
-            whatToDo = AfxMessageBox("You are about to destroy your ticket(s)/token(s)!",
-                                     MB_ICONEXCLAMATION|MB_YESNO, 0);
+        whatToDo = AfxMessageBox("Are you sure you want to destroy these tickets?",
+                                    MB_ICONEXCLAMATION|MB_YESNO, 0);
 
         if (whatToDo == IDYES)
         {
-            pLeash_kdestroy();
-            ResetTreeNodes();
+            // grab list mutex
+            elem = m_ccacheDisplay;
+            while (elem) {
+                if (elem->m_selected)
+                    kdestroy(elem->m_ccacheName);
+                elem = elem->m_next;
+            }
+            // release list mutex
             SendMessage(WM_COMMAND, ID_UPDATE_DISPLAY, 0);
         }
     }
@@ -823,6 +869,26 @@ VOID CLeashView::OnDestroyTicket()
     m_autoRenewalAttempted = 0;
 }
 
+VOID CLeashView::OnMakeDefault()
+{
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    int code = 0;
+    krb5_context ctx;
+    krb5_ccache cc;
+    while (elem) {
+        if (elem->m_selected) {
+            pkrb5_init_context(&ctx);
+            code = pkrb5_cc_resolve(ctx, elem->m_ccacheName, &cc);
+            if (!code)
+                code = pkrb5_cc_switch(ctx, cc);
+            pkrb5_free_context(ctx);
+            CLeashApp::m_bUpdateDisplay = TRUE;
+            break;
+        }
+        elem = elem->m_next;
+    }
+}
+
 VOID CLeashView::OnChangePassword()
 {
     if (!m_hWnd)
@@ -839,20 +905,14 @@ VOID CLeashView::OnChangePassword()
     char username[64];
     char realm[192];
     char * principal = ticketinfo.Krb5.principal;
-    if (!*principal)
-	principal = ticketinfo.Krb4.principal;
     int i=0, j=0;
-    for (; principal[i] && principal[i] != '@'; i++)
-    {
-	username[i] = principal[i];
-    }
+    if (principal)
+        for (; principal[i] && principal[i] != '@'; i++)
+	        username[i] = principal[i];
     username[i] = '\0';
-    if (principal[i]) {
-	for (i++ ; principal[i] ; i++, j++)
-	{
-	    realm[j] = principal[i];
-	}
-    }
+    if (principal && principal[i])
+	    for (i++ ; principal[i] ; i++, j++)
+	        realm[j] = principal[i];
     realm[j] = '\0';
     ReleaseMutex(ticketinfo.lockObj);
 
@@ -874,17 +934,145 @@ VOID CLeashView::OnChangePassword()
     }
 }
 
+static CCacheDisplayData **
+FindCCacheDisplayData(const char * ccacheName, CCacheDisplayData **pList)
+{
+    CCacheDisplayData *elem;
+    while ((elem = *pList)) {
+        if (strcmp(ccacheName, elem->m_ccacheName)==0)
+            return pList;
+        pList = &elem->m_next;
+    }
+    return NULL;
+}
+
+void CLeashView::AddDisplayItem(CListCtrl &list,
+                                CCacheDisplayData *elem,
+                                int iItem,
+                                char *principal,
+                                long issued,
+                                long valid_until,
+                                long renew_until,
+                                char *encTypes,
+                                unsigned long flags)
+{
+    TCHAR* localTimeStr=NULL;
+    TCHAR* durationStr=NULL;
+    TCHAR tempStr[MAX_DURATION_STR+1];
+    int imageIndex;
+    time_t now = LeashTime();
+    if (iItem != elem->m_index)
+        imageIndex = -1;
+    else if (elem->m_expanded)
+        imageIndex = 0;
+    else
+        imageIndex = 2;
+
+    list.InsertItem(iItem, principal, imageIndex);
+
+    int iSubItem = 1;
+    if (sm_viewColumns[TIME_ISSUED].m_enabled) {
+        if (issued == 0) {
+            list.SetItemText(iItem, iSubItem++, "Unknown");
+        } else {
+            krb5TimestampToLocalizedString(issued, &localTimeStr);
+            list.SetItemText(iItem, iSubItem++, localTimeStr);
+        }
+    }
+    if (sm_viewColumns[RENEWABLE_UNTIL].m_enabled) {
+        if (valid_until == 0) {
+            list.SetItemText(iItem, iSubItem++, "Unknown");
+        } else if (valid_until < now) {
+            list.SetItemText(iItem, iSubItem++, "Expired");
+        } else if (renew_until) {
+            krb5TimestampToLocalizedString(renew_until, &localTimeStr);
+            DurationToString(renew_until - now, &durationStr);
+            if (localTimeStr && durationStr) {
+                _snprintf(tempStr, MAX_DURATION_STR, "%s %s", localTimeStr, durationStr);
+                tempStr[MAX_DURATION_STR] = 0;
+                list.SetItemText(iItem, iSubItem++, tempStr);
+            }
+        } else {
+            list.SetItemText(iItem, iSubItem++, "Not renewable");
+        }
+    }
+    if (sm_viewColumns[VALID_UNTIL].m_enabled) {
+        if (valid_until == 0) {
+            list.SetItemText(iItem, iSubItem++, "Unknown");
+        } else if (valid_until < now) {
+            list.SetItemText(iItem, iSubItem++, "Expired");
+        } else {
+            krb5TimestampToLocalizedString(valid_until, &localTimeStr);
+            DurationToString(valid_until - now, &durationStr);
+            if (localTimeStr && durationStr) {
+                _snprintf(tempStr, MAX_DURATION_STR, "%s %s", localTimeStr, durationStr);
+                tempStr[MAX_DURATION_STR] = 0;
+                list.SetItemText(iItem, iSubItem++, tempStr);
+            }
+        }
+    }
+
+    if (sm_viewColumns[ENCRYPTION_TYPE].m_enabled) {
+        list.SetItemText(iItem, iSubItem++, encTypes);
+    }
+    if (sm_viewColumns[TICKET_FLAGS].m_enabled) {
+        list.SetItemText(iItem, iSubItem++, "ticket flags here");
+    }
+    if (localTimeStr)
+        free(localTimeStr);
+    if (durationStr)
+        free(durationStr);
+}
+
+BOOL CLeashView::IsExpanded(TICKETINFO *info)
+{
+    CCacheDisplayData **pElem = FindCCacheDisplayData(info->ccache_name,
+                                                      &m_ccacheDisplay);
+    return (pElem && (*pElem)->m_expanded) ? TRUE : FALSE;
+}
+
+BOOL CLeashView::IsExpired(TICKETINFO *info)
+{
+    return LeashTime() > info->valid_until ? TRUE : FALSE;
+}
+
+BOOL CLeashView::IsExpired(TicketList *ticket)
+{
+    return LeashTime() > ticket->valid_until ? TRUE : FALSE;
+}
+
 VOID CLeashView::OnUpdateDisplay()
 {
     BOOL AfsEnabled = m_pApp->GetProfileInt("Settings", "AfsStatus", 1);
 
     CListCtrl& list = GetListCtrl();
+    // @TODO: there is probably a more sensible place to initialize these...
+    if ((m_BaseFont == NULL) && (list.GetFont())) {
+        m_BaseFont = *list.GetFont();
+        m_BoldFont = CreateBoldFont(m_BaseFont);
+        m_ItalicFont = CreateItalicFont(m_BaseFont);
+    }
+    // Determine currently focused item
+    int focusItem = list.GetNextItem(-1, LVNI_FOCUSED);
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    while (elem) {
+        if (focusItem >= elem->m_index) {
+            elem->m_focus = focusItem - elem->m_index;
+            focusItem = -1;
+        } else {
+            elem->m_focus = -1;
+        }
+        elem = elem->m_next;
+    }
+
     list.DeleteAllItems();
     ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
 	UpdateWindow();
     // Delete all of the columns.
     while (list.DeleteColumn(0));
 
+    list.SetImageList(&m_imageList, LVSIL_SMALL);
+
     // Reconstruct based on current options
     int columnIndex = 0;
     int itemIndex = 0;
@@ -899,16 +1087,6 @@ VOID CLeashView::OnUpdateDisplay()
         }
     }
 
-    m_pImageList = &m_imageList;
-    if (!m_pImageList)
-    {
-        AfxMessageBox("There is a problem finding images for the Ticket Tree!",
-                   MB_OK|MB_ICONSTOP);
-        return;
-    }
-
-    TV_INSERTSTRUCT m_tvinsert;
-
 #ifndef NO_KRB4
     INT ticketIconStatusKrb4;
     INT ticketIconStatus_SelectedKrb4;
@@ -926,7 +1104,6 @@ VOID CLeashView::OnUpdateDisplay()
 #ifndef NO_KRB4
     LONG krb4Error;
 #endif
-    LONG krb5Error;
     LONG afsError;
 
     if (WaitForSingleObject( ticketinfo.lockObj, 100 ) != WAIT_OBJECT_0)
@@ -938,24 +1115,20 @@ VOID CLeashView::OnUpdateDisplay()
 #endif
 
     // Get Kerb 5 tickets in list
-    krb5Error = pLeashKRB5GetTickets(&ticketinfo.Krb5, &m_listKrb5,
-                                     &CLeashApp::m_krbv5_context);
-    if (!krb5Error || krb5Error == KRB5_FCC_NOFILE)
+    LeashKRB5ListDefaultTickets(&ticketinfo.Krb5);
+    if (CLeashApp::m_hKrb5DLL && !CLeashApp::m_krbv5_profile)
     {
-        if (CLeashApp::m_hKrb5DLL && !CLeashApp::m_krbv5_profile)
+        CHAR confname[MAX_PATH];
+        if (CLeashApp::GetProfileFile(confname, sizeof(confname)))
         {
-            CHAR confname[MAX_PATH];
-            if (CLeashApp::GetProfileFile(confname, sizeof(confname)))
-            {
-                AfxMessageBox("Can't locate Kerberos Five Config. file!",
-                           MB_OK|MB_ICONSTOP);
-            }
-
-            const char *filenames[2];
-            filenames[0] = confname;
-            filenames[1] = NULL;
-            pprofile_init(filenames, &CLeashApp::m_krbv5_profile);
+            AfxMessageBox("Can't locate Kerberos Five Config. file!",
+                        MB_OK|MB_ICONSTOP);
         }
+
+        const char *filenames[2];
+        filenames[0] = confname;
+        filenames[1] = NULL;
+        pprofile_init(filenames, &CLeashApp::m_krbv5_profile);
     }
 
     // Get AFS Tokens in list
@@ -963,15 +1136,13 @@ VOID CLeashView::OnUpdateDisplay()
         char * principal;
         if ( ticketinfo.Krb5.principal[0] )
             principal = ticketinfo.Krb5.principal;
-        else if ( ticketinfo.Krb4.principal[0] )
-            principal = ticketinfo.Krb4.principal;
         else
             principal = "";
         afsError = pLeashAFSGetToken(&ticketinfo.Afs, &m_listAfs, principal);
     }
 
     /*
-     * Update Ticket Status for Krb4 and Krb5 so that we may use their state
+     * Update Ticket Status for Krb5 so that we may use their state
      * to select the appropriate Icon for the Parent Node
      */
 
@@ -1012,8 +1183,9 @@ VOID CLeashView::OnUpdateDisplay()
     /* Krb5 */
     UpdateTicketTime(ticketinfo.Krb5);
     m_ticketStatusKrb5 = GetLowTicketStatus(5);
-    if (!m_listKrb5 || EXPIRED_TICKETS == ticketinfo.Krb5.btickets ||
-         m_ticketStatusKrb5 == ZERO_MINUTES_LEFT)
+    if ((!ticketinfo.Krb5.btickets) ||
+        EXPIRED_TICKETS == ticketinfo.Krb5.btickets ||
+        m_ticketStatusKrb5 == ZERO_MINUTES_LEFT)
     {
         ticketIconStatusKrb5 = EXPIRED_CLOCK;
         ticketIconStatus_SelectedKrb5 = EXPIRED_CLOCK;
@@ -1071,268 +1243,117 @@ VOID CLeashView::OnUpdateDisplay()
         iconStatusAfs = TICKET_NOT_INSTALLED;
     }
 
-    m_tvinsert.hParent = NULL;
-    m_tvinsert.hInsertAfter = TVI_LAST;
-    m_tvinsert.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
-    m_tvinsert.item.hItem = NULL;
-    m_tvinsert.item.state = 0;
-    m_tvinsert.item.stateMask = 0; //TVIS_EXPANDED;
-    m_tvinsert.item.cchTextMax = 6;
-
-    if (CLeashApp::m_hKrb5DLL && m_listKrb5) {
-        m_tvinsert.item.pszText = ticketinfo.Krb5.principal;
-        m_tvinsert.item.mask |= TVIF_TEXT;
+    int trayIcon = NONE_PARENT_NODE;
+    if (CLeashApp::m_hKrb5DLL && ticketinfo.Krb5.btickets) {
         switch ( iconStatusKrb5 ) {
         case ACTIVE_TICKET:
-            m_tvinsert.item.iSelectedImage = ACTIVE_PARENT_NODE;
-            break;
-        case LOW_TICKET:
-            m_tvinsert.item.iSelectedImage = LOW_PARENT_NODE;
-            break;
-        case EXPIRED_TICKET:
-            m_tvinsert.item.iSelectedImage = EXPIRED_PARENT_NODE;
-            break;
-        }
-////
-#ifndef NO_KRB4
-    } else if (CLeashApp::m_hKrb4DLL && m_listKrb4) {
-        m_tvinsert.item.pszText = ticketinfo.Krb4.principal;
-        m_tvinsert.item.mask |= TVIF_TEXT;
-        switch ( iconStatusKrb4 ) {
-        case ACTIVE_TICKET:
-            m_tvinsert.item.iSelectedImage = ACTIVE_PARENT_NODE;
+            trayIcon = ACTIVE_PARENT_NODE;
             break;
         case LOW_TICKET:
-            m_tvinsert.item.iSelectedImage = LOW_PARENT_NODE;
+            trayIcon = LOW_PARENT_NODE;
             break;
         case EXPIRED_TICKET:
-            m_tvinsert.item.iSelectedImage = EXPIRED_PARENT_NODE;
+            trayIcon = EXPIRED_PARENT_NODE;
             break;
         }
-#endif
-    } else {
-        m_tvinsert.item.iSelectedImage = NONE_PARENT_NODE;
-        m_tvinsert.item.pszText = NULL;
     }
-    m_tvinsert.item.iImage = m_tvinsert.item.iSelectedImage;
-    m_tvinsert.item.cChildren = 0;
-    m_tvinsert.item.lParam = 0;
-    m_tvinsert.hParent = NULL;
-
-    SetTrayIcon(NIM_MODIFY, m_tvinsert.item.iImage);
-
-    // Krb5
-    m_tvinsert.hParent = m_hPrincipal;
-
-    if (CLeashApp::m_hKrb5DLL)
-    {
-        // kerb5 installed
-        m_tvinsert.item.pszText = "Kerberos Five Tickets";
-        m_tvinsert.item.iImage = iconStatusKrb5;
-        m_tvinsert.item.iSelectedImage = iconStatusKrb5;
-    }
-    else
-    {
-        // kerb5 not installed
-        ticketinfo.Krb5.btickets = NO_TICKETS;
-        m_tvinsert.item.pszText = "Kerberos Five Tickets (Not Available)";
-        m_tvinsert.item.iImage = TICKET_NOT_INSTALLED;
-        m_tvinsert.item.iSelectedImage = TICKET_NOT_INSTALLED;
-    }
-
-    TicketList* tempList = m_listKrb5, *killList;
-    while (tempList)
-    {
-        m_tvinsert.hParent = m_hKerb5;
-        m_tvinsert.item.iImage = ticketIconStatusKrb5;
-        m_tvinsert.item.iSelectedImage = ticketIconStatus_SelectedKrb5;
-        m_tvinsert.item.pszText = tempList->theTicket;
-
-        if ( tempList->tktEncType ) {
-            m_tvinsert.hParent = m_hk5tkt;
-            m_tvinsert.item.iImage = TKT_ENCRYPTION;
-            m_tvinsert.item.iSelectedImage = TKT_ENCRYPTION;
-            m_tvinsert.item.pszText = tempList->tktEncType;
-        }
-        if ( tempList->keyEncType ) {
-            m_tvinsert.hParent = m_hk5tkt;
-            m_tvinsert.item.iImage = TKT_SESSION;
-            m_tvinsert.item.iSelectedImage = TKT_SESSION;
-            m_tvinsert.item.pszText = tempList->keyEncType;
+    SetTrayIcon(NIM_MODIFY, trayIcon);
+
+    CCacheDisplayData* prevCCacheDisplay = m_ccacheDisplay;
+    m_ccacheDisplay = NULL;
+
+    const char *def_ccache_name = ticketinfo.Krb5.ccache_name;
+    TICKETINFO *principallist = NULL;
+    LeashKRB5ListAllTickets(&principallist);
+    int iItem = 0;
+    TicketList* tempList;
+    TICKETINFO *principal = principallist;
+    while (principal != NULL) {
+        CCacheDisplayData **pOldElem;
+        pOldElem = FindCCacheDisplayData(principal->ccache_name,
+                                         &prevCCacheDisplay);
+        if (pOldElem) {
+            // remove from old list
+            elem = *pOldElem;
+            *pOldElem = elem->m_next;
+            elem->m_next = NULL;
+        } else {
+            elem = new CCacheDisplayData(principal->ccache_name);
         }
-
-        if ( tempList->addrCount && tempList->addrList ) {
-            for ( int n=0; n<tempList->addrCount; n++ ) {
-                m_tvinsert.hParent = m_hk5tkt;
-                m_tvinsert.item.iImage = TKT_ADDRESS;
-                m_tvinsert.item.iSelectedImage = TKT_ADDRESS;
-                m_tvinsert.item.pszText = tempList->addrList[n];
-//                m_pTree->InsertItem(&m_tvinsert);
+        elem->m_isDefault = def_ccache_name &&
+                            (strcmp(def_ccache_name, elem->m_ccacheName) == 0);
+        elem->m_isRenewable = principal->renew_until != 0;
+
+        elem->m_next = m_ccacheDisplay;
+        m_ccacheDisplay = elem;
+        elem->m_index = iItem;
+
+        AddDisplayItem(list,
+                       elem,
+                       iItem++,
+                       principal->principal,
+                       principal->issued,
+                       principal->valid_until,
+                       principal->renew_until,
+                       "",
+                       principal->flags);
+        if (elem->m_expanded) {
+            for (tempList = principal->ticket_list;
+                 tempList != NULL;
+                 tempList = tempList->next) {
+                AddDisplayItem(list,
+                               elem,
+                               iItem++,
+                               tempList->service,
+                               tempList->issued,
+                               tempList->valid_until,
+                               tempList->renew_until,
+                               tempList->encTypes,
+                               tempList->flags);
             }
         }
-        tempList = tempList->next;
-    }
-
-    pLeashFreeTicketList(&m_listKrb5);
-
-//    if (m_hKerb5State == NODE_IS_EXPANDED)
-//        m_pTree->Expand(m_hKerb5, TVE_EXPAND);
-
-    // Krb4
-    m_tvinsert.hParent = m_hPrincipal;
-
-#ifndef NO_KRB4
-    if (CLeashApp::m_hKrb4DLL)
-    {
-        m_tvinsert.item.pszText = "Kerberos Four Tickets";
-        m_tvinsert.item.iImage = iconStatusKrb4;
-        m_tvinsert.item.iSelectedImage = iconStatusKrb4;
-    }
-    else
-    {
-#endif
-////Can this be removed altogether?
-        ticketinfo.Krb4.btickets = NO_TICKETS;
-        m_tvinsert.item.pszText = "Kerberos Four Tickets (Not Available)";
-        m_tvinsert.item.iImage = TICKET_NOT_INSTALLED;
-        m_tvinsert.item.iSelectedImage = TICKET_NOT_INSTALLED;
-#ifndef NO_KRB4
-    }
-#endif
-
-#ifndef NO_KRB4
-    m_hKerb4 = m_pTree ->InsertItem(&m_tvinsert);
-
-    if (m_hPrincipalState == NODE_IS_EXPANDED)
-        m_pTree->Expand(m_hPrincipal, TVE_EXPAND);
-
-    m_tvinsert.hParent = m_hKerb4;
-    m_tvinsert.item.iImage = ticketIconStatusKrb4;
-    m_tvinsert.item.iSelectedImage = ticketIconStatus_SelectedKrb4;
-
-
-////What does the original do?
-    tempList = m_listKrb4, *killList;
-    while (tempList)
-    {
-        m_tvinsert.item.pszText = tempList->theTicket;
-        m_pTree->InsertItem(&m_tvinsert);
-        tempList = tempList->next;
-    }
-
-    pLeashFreeTicketList(&m_listKrb4);
-
-    if (m_hKerb4State == NODE_IS_EXPANDED)
-        m_pTree->Expand(m_hKerb4, TVE_EXPAND);
-#endif
-
-    // AFS
-    m_tvinsert.hParent = m_hPrincipal;
-
-    if (!CLeashApp::m_hAfsDLL)
-    { // AFS service not started or just no tickets
-        m_tvinsert.item.pszText = "AFS Tokens (Not Available)";
-        m_tvinsert.item.iImage = TICKET_NOT_INSTALLED;
-        m_tvinsert.item.iSelectedImage = TICKET_NOT_INSTALLED;
-    }
-
-    if (!afsError && CLeashApp::m_hAfsDLL && m_tvinsert.item.pszText)
-    { // AFS installed
-
-        if (AfsEnabled)
-        {
-            m_tvinsert.item.pszText = "AFS Tokens";
-            m_tvinsert.item.iImage = iconStatusAfs;
-            m_tvinsert.item.iSelectedImage = iconStatusAfs;
-        }
-	else
-        {
-            m_tvinsert.item.pszText = "AFS Tokens (Disabled)";
-            m_tvinsert.item.iImage = TICKET_NOT_INSTALLED;
-            m_tvinsert.item.iSelectedImage = TICKET_NOT_INSTALLED;
+        if ((elem->m_focus >= 0) &&
+            (iItem > elem->m_index + elem->m_focus)) {
+            list.SetItemState(elem->m_index + elem->m_focus, LVIS_FOCUSED,
+                              LVIS_FOCUSED);
         }
-
-        m_tvinsert.hParent = m_hAFS;
-        m_tvinsert.item.iImage = ticketIconStatusAfs;
-        m_tvinsert.item.iSelectedImage = ticketIconStatus_SelectedAfs;
-
-        tempList = m_listAfs, *killList;
-        while (tempList)
-        {
-            m_tvinsert.item.pszText = tempList->theTicket;
-            tempList = tempList->next;
+        if (elem->m_selected)
+            list.SetItemState(elem->m_index, LVIS_SELECTED, LVIS_SELECTED);
+
+        principal = principal->next;
+    }
+
+    // create list item font data array
+    if (m_aListItemInfo != NULL)
+        delete[] m_aListItemInfo;
+    m_aListItemInfo = new ListItemInfo[iItem];
+    iItem = 0;
+    for (principal = principallist; principal != NULL;
+         principal = principal->next) {
+        //
+        HFONT font = IsExpired(principal) ? m_ItalicFont : m_BaseFont;
+        m_aListItemInfo[iItem++].m_font = font;
+        if (IsExpanded(principal)) {
+            for (TicketList *ticket = principal->ticket_list;
+                 ticket != NULL; ticket = ticket->next) {
+                HFONT font = IsExpired(ticket) ? m_ItalicFont : m_BaseFont;
+                m_aListItemInfo[iItem++].m_font = font;
+            }
         }
-
-        pLeashFreeTicketList(&m_listAfs);
-    }
-    else if (!afsError && CLeashApp::m_hAfsDLL && !m_tvinsert.item.pszText)
-    {
-        m_tvinsert.item.pszText = "AFS Tokens";
-        m_tvinsert.item.iImage = EXPIRED_TICKET;;
-        m_tvinsert.item.iSelectedImage = EXPIRED_TICKET;
     }
 
-    if (m_startup)
-    {
-        //m_startup = FALSE;
-        UpdateTicketTime(ticketinfo.Krb4);
+    // delete ccache items that no longer exist
+    while (prevCCacheDisplay != NULL) {
+        CCacheDisplayData *next = prevCCacheDisplay->m_next;
+        delete prevCCacheDisplay;
+        prevCCacheDisplay = next;
     }
 
-    CString sPrincipal = ticketinfo.Krb5.principal;
-    if (sPrincipal.IsEmpty())
-        sPrincipal = ticketinfo.Krb4.principal;
+    LeashKRB5FreeTicketInfo(&ticketinfo.Krb5);
+    LeashKRB5FreeTickets(&principallist);
 
-	// if no tickets
-	if (!ticketinfo.Krb4.btickets && !ticketinfo.Krb5.btickets)
-		sPrincipal = " No Tickets ";
+    // @TODO: AFS-specific here
 
-	// if no tickets and tokens
-    if (!ticketinfo.Krb4.btickets && !ticketinfo.Krb5.btickets && !ticketinfo.Afs.btickets) //&& sPrincipal.IsEmpty())
-    {
-        // No tickets
-        m_tvinsert.hParent = NULL;
-        m_tvinsert.item.pszText = " No Tickets/Tokens ";
-        m_tvinsert.item.iImage = NONE_PARENT_NODE;
-        m_tvinsert.item.iSelectedImage = NONE_PARENT_NODE;
-
-/*        if (CMainFrame::m_wndToolBar)
-        {
-            CToolBarCtrl *_toolBar = NULL;
-            CToolBarCtrl& toolBar = CMainFrame::m_wndToolBar.GetToolBarCtrl();
-            _toolBar = &toolBar;
-            if (_toolBar)
-            {
-                toolBar.SetState(ID_DESTROY_TICKET, TBSTATE_INDETERMINATE);
-            }
-            else
-            {
-                AfxMessageBox("There is a problem with the Leash Toolbar!",
-                           MB_OK|MB_ICONSTOP);
-            }
-        }
-*/
-    }
-    else
-    {
-        // We have some tickets
-//        m_pTree->SetItemText(m_hPrincipal, sPrincipal);
-/*
-        if (CMainFrame::m_wndToolBar)
-        {
-            CToolBarCtrl *_toolBar = NULL;
-            CToolBarCtrl& toolBar = CMainFrame::m_wndToolBar.GetToolBarCtrl();
-            _toolBar = &toolBar;
-            if (_toolBar)
-            {
-                toolBar.SetState(ID_DESTROY_TICKET, TBSTATE_ENABLED);
-            }
-            else
-            {
-                AfxMessageBox("There is a problem with the Leash Toolbar!", MB_OK|MB_ICONSTOP);
-            }
-        }
-*/
-    }
     ReleaseMutex(ticketinfo.lockObj);
 }
 
@@ -1534,7 +1555,9 @@ void CLeashView::ToggleViewColumn(eViewColumn viewOption)
     info.m_enabled = !info.m_enabled;
     if (m_pApp)
         m_pApp->WriteProfileInt("Settings", info.m_name, info.m_enabled);
-    OnUpdateDisplay();
+    // Don't update display immediately; wait for next idle so our
+    // checkbox controls will be more responsive
+    CLeashApp::m_bUpdateDisplay = TRUE;
 }
 
 VOID CLeashView::OnRenewableUntil()
@@ -1738,7 +1761,7 @@ VOID CLeashView::OnDestroy()
     CListView::OnDestroy();
     if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
         throw("Unable to lock ticketinfo");
-    BOOL b_destroy = m_destroyTicketsOnExit && (ticketinfo.Krb4.btickets || ticketinfo.Krb5.btickets);
+    BOOL b_destroy = m_destroyTicketsOnExit && ticketinfo.Krb5.btickets;
     ReleaseMutex(ticketinfo.lockObj);
 
     if (b_destroy)
@@ -1753,20 +1776,18 @@ VOID CLeashView::OnDestroy()
 
 VOID CLeashView::OnUpdateDestroyTicket(CCmdUI* pCmdUI)
 {
-    if (!CLeashApp::m_hAfsDLL)
-        pCmdUI->SetText("&Destroy Ticket(s)\tCtrl+D");
-    else
-        pCmdUI->SetText("&Destroy Ticket(s)/Token(s)\tCtrl+D");
-
-    if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock ticketinfo");
-    BOOL b_enable =!ticketinfo.Krb4.btickets && !ticketinfo.Krb5.btickets && !ticketinfo.Afs.btickets;
-    ReleaseMutex(ticketinfo.lockObj);
+    // @TODO: mutex
+    BOOL enable = FALSE;
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    while (elem != NULL) {
+        if (elem->m_selected) {
+            enable = TRUE;
+            break;
+        }
+        elem = elem->m_next;
+    }
 
-    if (b_enable)
-        pCmdUI->Enable(FALSE);
-    else
-        pCmdUI->Enable(TRUE);
+    pCmdUI->Enable(enable);
 }
 
 VOID CLeashView::OnUpdateInitTicket(CCmdUI* pCmdUI)
@@ -1790,39 +1811,24 @@ VOID CLeashView::OnUpdateInitTicket(CCmdUI* pCmdUI)
 
 VOID CLeashView::OnUpdateRenewTicket(CCmdUI* pCmdUI)
 {
-    if (!CLeashApp::m_hAfsDLL)
-        pCmdUI->SetText("&Renew Ticket(s)\tCtrl+R");
-    else
-        pCmdUI->SetText("&Renew Ticket(s)/Token(s)\tCtrl+R");
-
-    if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock ticketinfo");
-    BOOL b_enable = !(
-#ifndef NO_KRB4
-	ticketinfo.Krb4.btickets ||
-#endif
-	ticketinfo.Krb5.btickets) ||
-////Not sure about the boolean logic here
-#ifndef NO_KRB4
-                    !CLeashApp::m_hKrb4DLL &&
-#endif
-		    !CLeashApp::m_hKrb5DLL && !CLeashApp::m_hAfsDLL;
-    ReleaseMutex(ticketinfo.lockObj);
+    // @TODO: mutex
+    BOOL enable = FALSE;
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    while (elem != NULL) {
+        if (elem->m_selected) { // @TODO: && elem->m_renewable
+            enable = TRUE;
+            break;
+        }
+        elem = elem->m_next;
+    }
 
-    if (b_enable)
-        pCmdUI->Enable(FALSE);
-    else
-        pCmdUI->Enable(TRUE);
+    pCmdUI->Enable(enable);
 }
 
 VOID CLeashView::OnUpdateImportTicket(CCmdUI* pCmdUI)
 {
     bool ccIsMSLSA = false;
 
-#ifndef KRB5_TC_NOTICKET
-    if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-        throw("Unable to lock TGS request mutex");
-#endif
     if (CLeashApp::m_krbv5_context)
     {
         const char *ccName = pkrb5_cc_default_name(CLeashApp::m_krbv5_context);
@@ -1835,9 +1841,6 @@ VOID CLeashView::OnUpdateImportTicket(CCmdUI* pCmdUI)
         pCmdUI->Enable(FALSE);
     else
         pCmdUI->Enable(TRUE);
-#ifndef KRB5_TC_NOTICKET
-    ReleaseMutex(m_tgsReqMutex);
-#endif
 }
 
 LRESULT CLeashView::OnGoodbye(WPARAM wParam, LPARAM lParam)
@@ -1885,10 +1888,6 @@ LRESULT CLeashView::OnTrayIcon(WPARAM wParam, LPARAM lParam)
                 menu->AppendMenu(MF_STRING, ID_LEASH_RESTORE, "&Open Leash Window");
             menu->AppendMenu(MF_SEPARATOR);
             menu->AppendMenu(MF_STRING, ID_INIT_TICKET, "&Get Tickets");
-#ifndef KRB5_TC_NOTICKET
-            if (WaitForSingleObject( m_tgsReqMutex, INFINITE ) != WAIT_OBJECT_0)
-                throw("Unable to lock TGS request mutex");
-#endif
             if (WaitForSingleObject( ticketinfo.lockObj, INFINITE ) != WAIT_OBJECT_0)
                 throw("Unable to lock ticketinfo");
             if (!(
@@ -1911,14 +1910,11 @@ LRESULT CLeashView::OnTrayIcon(WPARAM wParam, LPARAM lParam)
             else
                 nFlags = MF_STRING;
             menu->AppendMenu(MF_STRING, ID_IMPORT_TICKET, "&Import Tickets");
-            if (!ticketinfo.Krb4.btickets && !ticketinfo.Krb5.btickets && !ticketinfo.Afs.btickets)
+            if (!ticketinfo.Krb5.btickets && !ticketinfo.Afs.btickets)
                 nFlags = MF_STRING | MF_GRAYED;
             else
                 nFlags = MF_STRING;
             ReleaseMutex(ticketinfo.lockObj);
-#ifndef KRB5_TC_NOTICKET
-            ReleaseMutex(m_tgsReqMutex);
-#endif
             menu->AppendMenu(MF_STRING, ID_DESTROY_TICKET, "&Destroy Tickets");
             menu->AppendMenu(MF_STRING, ID_CHANGE_PASSWORD, "&Change Password");
 
@@ -2169,11 +2165,9 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
         try {
         if (InterlockedDecrement(&m_timerMsgNotInProgress) == 0) {
 
-            CString ticketStatusKrb4 = TCHAR(NOT_INSTALLED);
             CString ticketStatusKrb5 = TCHAR(NOT_INSTALLED);
             CString ticketStatusAfs  = TCHAR(NOT_INSTALLED);
             CString strTimeDate;
-            CString lowTicketWarningKrb4;
             CString lowTicketWarningKrb5;
             CString lowTicketWarningAfs;
 
@@ -2272,115 +2266,6 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
             }
             //KRB5
 
-#ifndef NO_KRB4
-            if (CLeashApp::m_hKrb4DLL)
-            {
-                // KRB4
-                UpdateTicketTime(ticketinfo.Krb4);
-                if (!ticketinfo.Krb4.btickets)
-                {
-                    ticketStatusKrb4 = "Kerb-4: No Tickets";
-                }
-                else if (EXPIRED_TICKETS == ticketinfo.Krb4.btickets)
-                {
-#ifndef NO_KRB5
-                    if (ticketinfo.Krb5.btickets &&
-                         EXPIRED_TICKETS != ticketinfo.Krb5.btickets &&
-                         m_autoRenewTickets &&
-                         !m_autoRenewalAttempted &&
-                         ticketinfo.Krb5.renew_till &&
-                         (ticketinfo.Krb5.issue_date + ticketinfo.Krb5.renew_till -LeashTime() > 20 * 60) &&
-                         pLeash_get_default_use_krb4()
-                         )
-                    {
-                        m_autoRenewalAttempted = 1;
-                        ReleaseMutex(ticketinfo.lockObj);
-                        AfxBeginThread(RenewTicket,m_hWnd);
-                        goto timer_start;
-                    }
-#endif /* NO_KRB5 */
-                    ticketStatusKrb4 = "Kerb-4: Expired Tickets";
-                    lowTicketWarningKrb4 = "Your Kerberos Four ticket(s) have expired";
-                    if (!m_warningOfTicketTimeLeftLockKrb4)
-                        m_warningOfTicketTimeLeftKrb4 = 0;
-                    m_warningOfTicketTimeLeftLockKrb4 = ZERO_MINUTES_LEFT;
-                    m_ticketTimeLeft = 0;
-                }
-                else if ( pLeash_get_default_use_krb4() )
-                {
-                    m_ticketStatusKrb4 = GetLowTicketStatus(4);
-                    switch (m_ticketStatusKrb4)
-                    {
-                    case FIFTEEN_MINUTES_LEFT:
-                        ticketinfo.Krb4.btickets = TICKETS_LOW;
-                        lowTicketWarningKrb4 = "Less then 15 minutes left on your Kerberos Four ticket(s)";
-                        break;
-                    case TEN_MINUTES_LEFT:
-                        ticketinfo.Krb4.btickets = TICKETS_LOW;
-                        lowTicketWarningKrb4 = "Less then 10 minutes left on your Kerberos Four ticket(s)";
-                        if (!m_warningOfTicketTimeLeftLockKrb4)
-                            m_warningOfTicketTimeLeftKrb4 = 0;
-                        m_warningOfTicketTimeLeftLockKrb4 = TEN_MINUTES_LEFT;
-                        break;
-                    case FIVE_MINUTES_LEFT:
-                        ticketinfo.Krb4.btickets = TICKETS_LOW;
-                        if (m_warningOfTicketTimeLeftLockKrb4 == TEN_MINUTES_LEFT)
-                            m_warningOfTicketTimeLeftKrb4 = 0;
-                        m_warningOfTicketTimeLeftLockKrb4 = FIVE_MINUTES_LEFT;
-                        lowTicketWarningKrb4 = "Less then 5 minutes left on your Kerberos Four ticket(s)";
-                        break;
-                    default:
-                        m_ticketStatusKrb4 = 0;
-                        break;
-                    }
-
-                }
-
-                if (CMainFrame::m_isMinimum)
-                {
-                    // minimized dispay
-                    ticketStatusKrb4.Format("Kerb-4: %02d:%02d Left",
-                                             (m_ticketTimeLeft / 60L / 60L),
-                                             (m_ticketTimeLeft / 60L % 60L));
-                }
-                else
-                {
-                    // normal display
-                    if (GOOD_TICKETS == ticketinfo.Krb4.btickets ||
-                         TICKETS_LOW == ticketinfo.Krb4.btickets)
-                    {
-                        if ( m_ticketTimeLeft >= 60 ) {
-                            ticketStatusKrb4.Format("Kerb-4 Ticket Life: %02d:%02d",
-                                                     (m_ticketTimeLeft / 60L / 60L),
-                                                     (m_ticketTimeLeft / 60L % 60L));
-                        } else {
-                            ticketStatusKrb4.Format("Kerb-4 Ticket Life: < 1 min");
-                        }
-                    }
-
-                    if (CMainFrame::m_wndStatusBar)
-                    {
-                        CMainFrame::m_wndStatusBar.SetPaneInfo(2, 111111, SBPS_NORMAL, 130);
-                        CMainFrame::m_wndStatusBar.SetPaneText(2, ticketStatusKrb4, SBT_POPOUT);
-                    }
-                }
-            }
-            else
-            {
-#endif
-////Should this be removed altogether?
-                // not installed
-                ticketStatusKrb4.Format("Kerb-4: Not Available");
-
-                if (CMainFrame::m_wndStatusBar)
-                {
-                    CMainFrame::m_wndStatusBar.SetPaneInfo(2, 111111, SBPS_NORMAL, 130);
-                    CMainFrame::m_wndStatusBar.SetPaneText(2, ticketStatusKrb4, SBT_POPOUT);
-                }
-#ifndef NO_KRB4
-            }
-            // KRB4
-#endif
 
             if (CLeashApp::m_hAfsDLL)
             {
@@ -2401,8 +2286,8 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
                          EXPIRED_TICKETS != ticketinfo.Krb5.btickets &&
                          m_autoRenewTickets &&
                          !m_autoRenewalAttempted &&
-                         ticketinfo.Krb5.renew_till &&
-                         (ticketinfo.Krb5.issue_date + ticketinfo.Krb5.renew_till -LeashTime() > 20 * 60) &&
+                         ticketinfo.Krb5.renew_until &&
+                         (ticketinfo.Krb5.issued + ticketinfo.Krb5.renew_until -LeashTime() > 20 * 60) &&
                          !stricmp(ticketinfo.Krb5.principal,ticketinfo.Afs.principal)
                          )
                     {
@@ -2485,26 +2370,12 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
 #endif
                 }
             }
-#ifdef COMMENT
-            // we do not set this field because the field does not exist when AfsDLL is NULL
-            else
-            {
-                // not installed
-                ticketStatusAfs.Format("AFS: Not Available");
-
-                if (CMainFrame::m_wndStatusBar)
-                {
-                    CMainFrame::m_wndStatusBar.SetPaneInfo(3, 111113, SBPS_NORMAL, 130);
-                    CMainFrame::m_wndStatusBar.SetPaneText(3, ticketStatusAfs, SBT_POPOUT);
-                }
-            }
-#endif /* COMMENT */
             // AFS
 
 #ifndef NO_KRB5
             if ( m_ticketStatusKrb5 == TWENTY_MINUTES_LEFT &&
-                 m_autoRenewTickets && !m_autoRenewalAttempted && ticketinfo.Krb5.renew_till &&
-                 (ticketinfo.Krb5.issue_date + ticketinfo.Krb5.renew_till - LeashTime() > 20 * 60))
+                 m_autoRenewTickets && !m_autoRenewalAttempted && ticketinfo.Krb5.renew_until &&
+                 (ticketinfo.Krb5.issued + ticketinfo.Krb5.renew_until - LeashTime() > 20 * 60))
             {
                 m_autoRenewalAttempted = 1;
                 ReleaseMutex(ticketinfo.lockObj);
@@ -2516,15 +2387,12 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
             BOOL warningKrb5 = m_ticketStatusKrb5 > NO_TICKETS &&
                 m_ticketStatusKrb5 < TWENTY_MINUTES_LEFT &&
                     !m_warningOfTicketTimeLeftKrb5;
-            BOOL warningKrb4 = m_ticketStatusKrb4 > NO_TICKETS &&
-                m_ticketStatusKrb4 < TWENTY_MINUTES_LEFT &&
-                    !m_warningOfTicketTimeLeftKrb4;
             BOOL warningAfs = m_ticketStatusAfs > NO_TICKETS &&
                 m_ticketStatusAfs < TWENTY_MINUTES_LEFT &&
                     !m_warningOfTicketTimeLeftAfs;
 
             // Play warning message only once per each case statement above
-            if (warningKrb4 || warningKrb5 || warningAfs)
+            if (warningKrb5 || warningAfs)
             {
 
                 CString lowTicketWarning = "";
@@ -2535,13 +2403,6 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
                     m_warningOfTicketTimeLeftKrb5 = ON;
                     warnings++;
                 }
-                if (warningKrb4) {
-                    if ( warnings )
-                        lowTicketWarning += "\n";
-                    lowTicketWarning += lowTicketWarningKrb4;
-                    m_warningOfTicketTimeLeftKrb4 = ON;
-                    warnings++;
-                }
                 if (warningAfs) {
                     if ( warnings )
                         lowTicketWarning += "\n";
@@ -2564,14 +2425,12 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
                 if ( CLeashApp::m_hAfsDLL )
                     strTimeDate = ( "Leash - "
                                     "[" + ticketStatusKrb5 + "] - " +
-                                    "[" + ticketStatusKrb4 + "] - " +
                                     "[" + ticketStatusAfs + "] - " +
                                     "[" + ticketinfo.Krb5.principal + "]" + " - " +
                                     tTimeDate.Format("%A, %B %d, %Y  %H:%M "));
                 else
                     strTimeDate = ( "Leash - "
                                     "[" + ticketStatusKrb5 + "] - " +
-                                    "[" + ticketStatusKrb4 + "] - " +
                                     "[" + ticketinfo.Krb5.principal + "]" + " - " +
                                     tTimeDate.Format("%A, %B %d, %Y  %H:%M "));
             }
@@ -2591,13 +2450,6 @@ BOOL CLeashView::PreTranslateMessage(MSG* pMsg)
                                     " - [" + ticketinfo.Krb5.principal + "]");
                 else
                     strTimeDate = "Leash: Kerb-5 No Tickets";
-            } else {
-                if ( ticketinfo.Krb4.btickets )
-                    strTimeDate = ( "Leash: "
-                                    "[" + ticketStatusKrb4 + "]" +
-                                    " - [" + ticketinfo.Krb4.principal + "]");
-                else
-                    strTimeDate = "Leash: Kerb-4 No Tickets";
             }
             ReleaseMutex(ticketinfo.lockObj);
 
@@ -2667,6 +2519,29 @@ VOID CLeashView::OnUpdateAutoRenew(CCmdUI* pCmdUI)
     pCmdUI->SetCheck(m_autoRenewTickets);
 }
 
+VOID CLeashView::OnUpdateMakeDefault(CCmdUI* pCmdUI)
+{
+    // enable if exactly one principal is selected and that principal is not
+    // the default principal
+    BOOL enable = FALSE;
+    CCacheDisplayData *elem = m_ccacheDisplay;
+    while (elem != NULL) {
+        if (elem->m_selected) {
+            if (enable) {
+                // multiple selection; disable button
+                enable = FALSE;
+                break;
+            }
+            if (elem->m_isDefault)
+                break;
+
+            enable = TRUE;
+        }
+        elem = elem->m_next;
+    }
+    pCmdUI->Enable(enable);
+}
+
 VOID CLeashView::AlarmBeep()
 {
 	if (m_lowTicketAlarmSound)
@@ -2781,7 +2656,7 @@ CLeashView::OnObtainTGTWithParam(WPARAM wParam, LPARAM lParam)
 	if ( *param )
 	    strcpy(ldi.in.ccache,param);
     } else {
-        strcpy(ldi.in.title,"Initialize Ticket");
+        strcpy(ldi.in.title,"Get Ticket");
     }
 
     res = pLeash_kinit_dlg_ex(m_hWnd, &ldi);
@@ -2789,3 +2664,162 @@ CLeashView::OnObtainTGTWithParam(WPARAM wParam, LPARAM lParam)
     ::SendMessage(m_hWnd, WM_COMMAND, ID_UPDATE_DISPLAY, 0);
     return res;
 }
+
+
+// Find the CCacheDisplayData corresponding to the specified item, if it exists
+static CCacheDisplayData *
+FindCCacheDisplayData(int item, CCacheDisplayData *elem)
+{
+    while (elem != NULL) {
+        if (elem->m_index == item)
+            break;
+        elem = elem->m_next;
+    }
+    return elem;
+}
+
+
+void CLeashView::OnLvnItemActivate(NMHDR *pNMHDR, LRESULT *pResult)
+{
+    LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
+    // TODO: Add your control notification handler code here
+    CCacheDisplayData *elem = FindCCacheDisplayData(pNMIA->iItem,
+                                                    m_ccacheDisplay);
+    if (elem != NULL) {
+        elem->m_expanded = !elem->m_expanded;
+        OnUpdateDisplay();
+    }
+    *pResult = 0;
+}
+
+
+void CLeashView::OnLvnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
+{
+    LPNMLVKEYDOWN pLVKeyDow = reinterpret_cast<LPNMLVKEYDOWN>(pNMHDR);
+    int expand = -1; // -1 = unchanged; 0 = collapse; 1 = expand
+    switch (pLVKeyDow->wVKey) {
+    case VK_RIGHT:
+        // expand focus item
+        expand = 1;
+        break;
+    case VK_LEFT:
+        // collapse focus item
+        expand = 0;
+        break;
+    default:
+        break;
+    }
+    if (expand >= 0) {
+        int focusedItem = GetListCtrl().GetNextItem(-1, LVNI_FOCUSED);
+        if (focusedItem >= 0) {
+            CCacheDisplayData *elem = FindCCacheDisplayData(focusedItem,
+                                                            m_ccacheDisplay);
+            if (elem != NULL) {
+                if (elem->m_expanded != expand) {
+                    elem->m_expanded = expand;
+                    OnUpdateDisplay();
+                }
+            }
+        }
+    }
+    *pResult = 0;
+}
+
+void CLeashView::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
+{
+    CCacheDisplayData *elem;
+    LRESULT result = 0;
+    LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
+    // TODO: Add your control notification handler code here
+    if ((pNMLV->uNewState ^ pNMLV->uOldState) & LVIS_SELECTED) {
+        // selection state changing
+        elem = FindCCacheDisplayData(pNMLV->iItem, m_ccacheDisplay);
+        if (elem == NULL) {
+            // this is an individual ticket, not a cache, so prevent selection
+            if (pNMLV->uNewState & LVIS_SELECTED) {
+                unsigned int newState =  pNMLV->uNewState & ~LVIS_SELECTED;
+                result = 1; // suppress changes
+                if (newState != pNMLV->uOldState) {
+                    // but need to make other remaining changes still
+                    GetListCtrl().SetItemState(pNMLV->iItem, newState,
+                                               newState ^ pNMLV->uOldState);
+                }
+            }
+        } else {
+            elem->m_selected = (pNMLV->uNewState & LVIS_SELECTED) ? 1 : 0;
+        }
+    }
+    *pResult = result;
+}
+
+CCacheDisplayData *
+FindCCacheDisplayElem(CCacheDisplayData *pElem, int itemIndex)
+{
+    while (pElem != NULL) {
+        if (pElem->m_index == itemIndex)
+            return pElem;
+        pElem = pElem->m_next;
+    }
+    return NULL;
+}
+
+HFONT CLeashView::GetSubItemFont(int iItem, int iSubItem)
+{
+    HFONT retval = m_BaseFont;
+    int iColumn, columnSubItem = 0;
+
+    // Translate subitem to column index
+    for (iColumn = 0; iColumn < NUM_VIEW_COLUMNS; iColumn++) {
+        if (sm_viewColumns[iColumn].m_enabled) {
+            if (columnSubItem == iSubItem)
+                break;
+            else
+                columnSubItem++;
+        }
+    }
+    switch (iColumn) {
+    case RENEWABLE_UNTIL:
+    case VALID_UNTIL:
+        retval = m_aListItemInfo[iItem].m_font;
+        break;
+    default:
+        break;
+    }
+    return retval;
+}
+
+void CLeashView::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
+{
+    HFONT font;
+    CCacheDisplayData *pElem;
+    *pResult = CDRF_DODEFAULT;
+    LPNMLVCUSTOMDRAW pNMLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
+    switch (pNMLVCD->nmcd.dwDrawStage) {
+    case CDDS_PREPAINT:
+        *pResult = CDRF_NOTIFYITEMDRAW;
+        break;
+    case CDDS_ITEMPREPAINT:
+        *pResult = CDRF_NOTIFYSUBITEMDRAW;
+        break;
+    case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
+        pElem = FindCCacheDisplayElem(m_ccacheDisplay,
+                                      pNMLVCD->nmcd.dwItemSpec);
+        if (pNMLVCD->iSubItem == 0) {
+            // set bold font if default princ
+            if (pElem && pElem->m_isDefault) {
+                font = m_BoldFont;
+            } else {
+                font = m_BaseFont;
+            }
+        } else {
+            // set italic font for 'valid until' and 'renewable until'
+            // columns if expired ticket
+            font = GetSubItemFont(pNMLVCD->nmcd.dwItemSpec, pNMLVCD->iSubItem);
+        }
+        SelectObject(pNMLVCD->nmcd.hdc, font);
+        *pResult = CDRF_NEWFONT;
+        break;
+    default:
+        break;
+    }
+}
diff --git a/src/windows/leash/LeashView.h b/src/windows/leash/LeashView.h
index cbdb77f..307c36d 100644
--- a/src/windows/leash/LeashView.h
+++ b/src/windows/leash/LeashView.h
@@ -86,6 +86,43 @@ enum eViewColumn {
     NUM_VIEW_COLUMNS
 };
 
+class CCacheDisplayData
+{
+public:
+    CCacheDisplayData(const char *ccache_name) :
+      m_next(NULL),
+      m_ccacheName(strdup(ccache_name)),
+      m_index(-1),
+      m_focus(-1),
+      m_expanded(0),
+      m_selected(0),
+      m_isRenewable(0),
+      m_isDefault(0)
+    {
+    }
+
+    ~CCacheDisplayData()
+    {
+        if (m_ccacheName)
+            free(m_ccacheName);
+    }
+
+    CCacheDisplayData *m_next;
+    char *m_ccacheName;
+    int m_index;               // item index in list view
+    int m_focus;               // sub-item with focus
+    unsigned int m_expanded;   // true when each individual ticket is displayed
+    unsigned int m_selected;   // true when this ccache is selected
+    unsigned int m_isRenewable; // true when tgt is renewable
+    unsigned int m_isDefault;  // true when this is the default ccache
+};
+
+struct ListItemInfo
+{
+    ListItemInfo() : m_font(NULL) {}
+    HFONT m_font;
+};
+
 class CLeashView : public CListView
 {
 private:
@@ -93,11 +130,10 @@ private:
 #ifndef NO_KRB4
     TicketList*         m_listKrb4;
 #endif
-    TicketList*         m_listKrb5;
     TicketList*         m_listAfs;
     CLeashDebugWindow*	m_pDebugWindow;
+    CCacheDisplayData*  m_ccacheDisplay;
 	CImageList			m_imageList;
-	CImageList			*m_pImageList;
 	CWinApp*			m_pApp;
 	HTREEITEM			m_hPrincipal;
 ////@#+Remove
@@ -127,6 +163,10 @@ private:
     CString*            m_pWarningMessage;
     BOOL                m_bIconAdded;
     BOOL                m_bIconDeleted;
+    HFONT               m_BaseFont;
+    HFONT               m_BoldFont;
+    HFONT               m_ItalicFont;
+    ListItemInfo*       m_aListItemInfo;
 
     static ViewColumnInfo sm_viewColumns[NUM_VIEW_COLUMNS];
 
@@ -159,6 +199,7 @@ private:
     VOID UpdateBars();
     VOID GetScrollBarSizes(CSize& sizeSb);
     BOOL GetTrueClientSize(CSize& size, CSize& sizeSb);
+    HFONT GetSubItemFont(int iItem, int iSubItem);
 
     //void   GetRowWidthHeight(CDC* pDC, LPCSTR theString, int& nRowWidth,
     //                         int& nRowHeight, int& nCharWidth);
@@ -168,6 +209,17 @@ private:
     static VOID	UpdateTicketTime(TICKETINFO& ticketinfo);
     static INT	GetLowTicketStatus(int);
     static time_t	LeashTime();
+    static BOOL IsExpired(TicketList *ticket);
+    static BOOL IsExpired(TICKETINFO *info);
+    static VOID AddDisplayItem(CListCtrl &list,
+                               CCacheDisplayData *elem,
+                               int iItem,
+                               char *principal,
+                               long issued,
+                               long valid_until,
+                               long renew_until,
+                               char *encTypes,
+                               unsigned long flags);
 
     void   SetTrayIcon(int nim, int state=0);
     void   SetTrayText(int nim, CString tip);
@@ -183,6 +235,8 @@ private:
     BOOL PostWarningMessage(const CString& message);
     afx_msg LRESULT OnWarningPopup(WPARAM wParam, LPARAM lParam);
 
+    BOOL    IsExpanded(TICKETINFO *);
+
 protected: // create from serialization only
 	DECLARE_DYNCREATE(CLeashView)
 
@@ -238,6 +292,7 @@ protected:
 	afx_msg VOID OnRenewTicket();
 	afx_msg VOID OnImportTicket();
 	afx_msg VOID OnDestroyTicket();
+	afx_msg VOID OnMakeDefault();
 	afx_msg VOID OnChangePassword();
 	afx_msg VOID OnUpdateDisplay();
 	afx_msg VOID OnSynTime();
@@ -264,6 +319,7 @@ protected:
 	afx_msg VOID OnUpdateKillTixOnExit(CCmdUI* pCmdUI);
 	afx_msg VOID OnUpdateLowTicketAlarm(CCmdUI* pCmdUI);
 	afx_msg VOID OnUpdateAutoRenew(CCmdUI* pCmdUI);
+	afx_msg VOID OnUpdateMakeDefault(CCmdUI* pCmdUI);
 	afx_msg VOID OnAppAbout();
 	afx_msg VOID OnAfsControlPanel();
 	afx_msg VOID OnUpdateDebugMode(CCmdUI* pCmdUI);
@@ -290,6 +346,11 @@ protected:
     afx_msg void OnItemChanged(NMHDR* pNmHdr, LRESULT* pResult);
     //}}AFX_MSG
 	DECLARE_MESSAGE_MAP()
+public:
+    afx_msg void OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);
+    afx_msg void OnLvnItemActivate(NMHDR *pNMHDR, LRESULT *pResult);
+    afx_msg void OnLvnKeydown(NMHDR *pNMHDR, LRESULT *pResult);
+    afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
 };
 
 /*
diff --git a/src/windows/leash/Lglobals.h b/src/windows/leash/Lglobals.h
index 7ad47a2..87fdd09 100644
--- a/src/windows/leash/Lglobals.h
+++ b/src/windows/leash/Lglobals.h
@@ -22,19 +22,7 @@
 ////#include <loadfuncs-krb.h>
 #include <loadfuncs-profile.h>
 #include <loadfuncs-leash.h>
-
-typedef struct TicketList
-{
-    char* theTicket;
-    TicketList* next;
-    char* tktEncType;
-    char* keyEncType;
-    int   addrCount;
-    char ** addrList;
-    char * name;
-    char * inst;
-    char * realm;
-} TicketList;
+#include <krb5.h>
 
 // toolhelp functions
 TYPEDEF_FUNC(
@@ -82,32 +70,18 @@ TYPEDEF_FUNC(
 TYPEDEF_FUNC(
     long,
     WINAPIV,
-    not_an_API_LeashKRB5GetTickets,
-    (TICKETINFO *, TicketList **, krb5_context *)
-    );
-TYPEDEF_FUNC(
-    long,
-    WINAPIV,
     not_an_API_LeashAFSGetToken,
     (TICKETINFO *, TicketList **, char *)
     );
 TYPEDEF_FUNC(
     long,
     WINAPIV,
-    not_an_API_LeashFreeTicketList,
-    (TicketList**)
-    );
-TYPEDEF_FUNC(
-    long,
-    WINAPIV,
     not_an_API_LeashGetTimeServerName,
     (char *, const char*)
     );
 
 extern DECL_FUNC_PTR(not_an_API_LeashKRB4GetTickets);
-extern DECL_FUNC_PTR(not_an_API_LeashKRB5GetTickets);
 extern DECL_FUNC_PTR(not_an_API_LeashAFSGetToken);
-extern DECL_FUNC_PTR(not_an_API_LeashFreeTicketList);
 extern DECL_FUNC_PTR(not_an_API_LeashGetTimeServerName);
 extern DECL_FUNC_PTR(Leash_kdestroy);
 extern DECL_FUNC_PTR(Leash_changepwd_dlg);
@@ -154,9 +128,7 @@ extern DECL_FUNC_PTR(Leash_reset_defaults);
 
 ////Do we still need this one?
 #define pLeashKRB4GetTickets     pnot_an_API_LeashKRB4GetTickets
-#define pLeashKRB5GetTickets     pnot_an_API_LeashKRB5GetTickets
 #define pLeashAFSGetToken        pnot_an_API_LeashAFSGetToken
-#define pLeashFreeTicketList     pnot_an_API_LeashFreeTicketList
 #define pLeashGetTimeServerName  pnot_an_API_LeashGetTimeServerName
 
 // psapi functions
@@ -266,6 +238,17 @@ extern BOOL SetRegistryVariable(const CString& regVariable,
 extern VOID LeashErrorBox(LPCSTR errorMsg, LPCSTR insertedString,
                           LPCSTR errorFlag = "Error");
 
+// Get ticket info for the default ccache only
+extern void LeashKRB5ListDefaultTickets(TICKETINFO *ticketinfo);
+// clean up ticket info
+extern void LeashKRB5FreeTicketInfo(TICKETINFO *ticketinfo);
+
+// Allocate TICKETINFO for each ccache that contain tickets
+extern void LeashKRB5ListAllTickets(TICKETINFO **ticketinfolist);
+// clean up ticket info list
+extern void LeashKRB5FreeTickets(TICKETINFO **ticketinfolist);
+
+
 
 class Directory
 {
@@ -283,8 +266,6 @@ public:
 class TicketInfoWrapper {
   public:
     HANDLE     lockObj;
-////Can this be commented out?
-    TICKETINFO Krb4;
     TICKETINFO Krb5;
     TICKETINFO Afs;
 };
diff --git a/src/windows/leash/Makefile.in b/src/windows/leash/Makefile.in
index 97439fd..99bdc76 100644
--- a/src/windows/leash/Makefile.in
+++ b/src/windows/leash/Makefile.in
@@ -51,7 +51,8 @@ OBJS=   \
 	$(OUTPRE)StdAfx.obj \
 	$(OUTPRE)AfsProperties.obj \
 	$(OUTPRE)VSroutines.obj \
-	$(OUTPRE)KrbMiscConfigOpt.obj
+	$(OUTPRE)KrbMiscConfigOpt.obj \
+	$(OUTPRE)KrbListTickets.obj
 
 RESFILE = $(OUTPRE)Leash.res
 XOBJS	= $(RESFILE)
@@ -66,7 +67,7 @@ LOCALINCLUDES= -I$(BUILDTOP) -I$(BUILDTOP)\include -I$(BUILDTOP)\windows\include
 RFLAGS	= $(LOCALINCLUDES)
 RCFLAGS	= $(RFLAGS) -D_WIN32 -DLEASH_APP
 
-DEFINES = -DWINSOCK -DWIN32 -DWINDOWS -D_AFXDLL -D_MBCS -DNO_KRB4 -DNO_STATUS_BAR
+DEFINES = -DWINSOCK -DWIN32 -DWINDOWS -D_AFXDLL -D_MBCS -DNO_KRB4 -DNO_STATUS_BAR -DUSE_MESSAGE_BOX
 !ifdef NODEBUG
 DEFINES = $(DEFINES)
 !else
diff --git a/src/windows/leash/res/ribbon1.mfcribbon-ms b/src/windows/leash/res/ribbon1.mfcribbon-ms
index 81338d1..fe8f5c6 100644
--- a/src/windows/leash/res/ribbon1.mfcribbon-ms
+++ b/src/windows/leash/res/ribbon1.mfcribbon-ms
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<AFX_RIBBON><HEADER><VERSION>1</VERSION></HEADER><RIBBON_BAR><ELEMENT_NAME>RibbonBar</ELEMENT_NAME><ENABLE_TOOLTIPS>TRUE</ENABLE_TOOLTIPS><ENABLE_TOOLTIPS_DESCRIPTION>TRUE</ENABLE_TOOLTIPS_DESCRIPTION><ENABLE_KEYS>TRUE</ENABLE_KEYS><ENABLE_PRINTPREVIEW>TRUE</ENABLE_PRINTPREVIEW><ENABLE_DRAWUSINGFONT>FALSE</ENABLE_DRAWUSINGFONT><BUTTON_MAIN><ELEMENT_NAME>Button_Main</ELEMENT_NAME><ID><NAME>ID_BUTTON2</NAME><VALUE>32813</VALUE></ID><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><IMAGE><ID><NAME>IDB_BITMAP2</NAME><VALUE>268</VALUE></ID></IMAGE></BUTTON_MAIN><CATEGORY_MAIN><ELEMENT_NAME>Category_Main</ELEMENT_NAME><NAME>Category1</NAME><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_APP_EXIT</NAME><VALUE>57665</VALUE></ID><TEXT>E&amp;xit</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_L!
 ARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_HELP_KERBEROS_</NAME><VALUE>32784</VALUE></ID><TEXT>&amp;Help</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS><RECENT_FILE_LIST><ENABLE>FALSE</ENABLE><LABEL>Recent Documents</LABEL><WIDTH>300</WIDTH></RECENT_FILE_LIST></CATEGORY_MAIN><CATEGORIES><CATEGORY><ELEMENT_NAME>Category</ELEMENT_NAME><NAME>Home</NAME><IMAGE_SMALL><ID><NAME>IDB_HOMESMALL</NAME><VALUE>266</VALUE></ID></IMAGE_SMALL><IMAGE_LARGE><ID><NAME>IDB_HOMELARGE</NAME><VALUE>267</VALUE></ID></IMAGE_LARGE><PANELS><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEM!
 ENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_!
 INIT_TIC
KET</NAME><VALUE>32807</VALUE></ID><TEXT>&amp;Get Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>TRUE</ALWAYS_LARGE><INDEX_SMALL>2</INDEX_SMALL><INDEX_LARGE>2</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_RENEW_TICKET</NAME><VALUE>32776</VALUE></ID><TEXT>&amp;Renew Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>TRUE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>3</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMEN!
 T_NAME>Button</ELEMENT_NAME><ID><NAME>ID_IMPORT_TICKET</NAME><VALUE>32806</VALUE></ID><TEXT>&amp;Import Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>4</INDEX_SMALL><INDEX_LARGE>4</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_DESTROY_TICKET</NAME><VALUE>32777</VALUE></ID><TEXT>&amp;Destroy Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>TRUE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><NAME>View</NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COL!
 UMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><!
 ELEMENT>
<ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_TIME_ISSUED</NAME><VALUE>32810</VALUE></ID><TEXT>&amp;Issued</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_RENEWABLE_UNTIL</NAME><VALUE>32811</VALUE></ID><TEXT>&amp;Renewable Until</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_VALID_UNTIL</NAME><VALUE>32828</VALUE></ID><TEXT>&amp;Valid Until</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_ENCRYPTI!
 ON_TYPE</NAME><VALUE>32826</VALUE></ID><TEXT>&amp;Encryption Type</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_SHOW_TICKET_FLAGS</NAME><VALUE>32812</VALUE></ID><TEXT>&amp;Flags</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><NAME>Options</NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_KILL_TIX_ONEXIT</NAME><VALUE>32785</VALUE></ID><TEXT>&amp;Destroy Tickets on Exit</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SM!
 ALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAUL!
 T_COMMAN
D></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_UPPERCASE_REALM</NAME><VALUE>32787</VALUE></ID><TEXT>Allow &amp;Mixed Case Realm Name</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_AUTO_RENEW</NAME><VALUE>32808</VALUE></ID><TEXT>Automatic Ticket &amp;Renewal</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_LOW_TICKET_ALARM</NAME><VALUE>32798</VALUE></ID><TEXT>Expiration &amp;Alarm</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT></ELEMENTS>!
 </PANEL></PANELS></CATEGORY></CATEGORIES></RIBBON_BAR></AFX_RIBBON>
+<AFX_RIBBON><HEADER><VERSION>1</VERSION></HEADER><RIBBON_BAR><ELEMENT_NAME>RibbonBar</ELEMENT_NAME><ENABLE_TOOLTIPS>TRUE</ENABLE_TOOLTIPS><ENABLE_TOOLTIPS_DESCRIPTION>TRUE</ENABLE_TOOLTIPS_DESCRIPTION><ENABLE_KEYS>TRUE</ENABLE_KEYS><ENABLE_PRINTPREVIEW>TRUE</ENABLE_PRINTPREVIEW><ENABLE_DRAWUSINGFONT>FALSE</ENABLE_DRAWUSINGFONT><BUTTON_MAIN><ELEMENT_NAME>Button_Main</ELEMENT_NAME><ID><NAME>ID_BUTTON2</NAME><VALUE>32813</VALUE></ID><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><IMAGE><ID><NAME>IDB_BITMAP2</NAME><VALUE>268</VALUE></ID></IMAGE></BUTTON_MAIN><CATEGORY_MAIN><ELEMENT_NAME>Category_Main</ELEMENT_NAME><NAME>Category1</NAME><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_HELP_KERBEROS_</NAME><VALUE>32784</VALUE></ID><TEXT>&amp;Help</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><I!
 NDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_APP_ABOUT</NAME><VALUE>57664</VALUE></ID><TEXT>&amp;About</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_APP_EXIT</NAME><VALUE>57665</VALUE></ID><TEXT>E&amp;xit</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS><RECENT_FILE_LIST><ENABLE>FALSE</ENABLE><LABEL>Recent Documents</LABEL><WIDTH>300</WIDTH></RECENT_FILE_LIST></CATEGORY_MAIN><CATEGORIES><CATEGORY><ELEMENT_NAME>Category</ELEMENT!
 _NAME><NAME>Home</NAME><IMAGE_SMALL><ID><NAME>IDB_HOMESMALL</N!
 AME><VAL
UE>266</VALUE></ID></IMAGE_SMALL><IMAGE_LARGE><ID><NAME>IDB_HOMELARGE</NAME><VALUE>267</VALUE></ID></IMAGE_LARGE><PANELS><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_INIT_TICKET</NAME><VALUE>32807</VALUE></ID><TEXT>&amp;Get Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>TRUE</ALWAYS_LARGE><INDEX_SMALL>2</INDEX_SMALL><INDEX_LARGE>2</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_RENEW_TICKET</NAME><VALUE>32776</VALUE></ID><TEXT>&amp;Renew Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>TRUE</ALWAYS_LARGE><!
 INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>3</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_IMPORT_TICKET</NAME><VALUE>32806</VALUE></ID><TEXT>&amp;Import Ticket</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>4</INDEX_SMALL><INDEX_LARGE>4</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_DESTROY_TICKET</NAME><VALUE>32777</VALUE></ID><TEXT>&amp;Destroy Ticket</TEXT><PALET!
 TE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>TRUE</ALWAYS_LARGE><IN!
 DEX_SMAL
L>-1</INDEX_SMALL><INDEX_LARGE>1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_MAKE_DEFAULT</NAME><VALUE>32835</VALUE></ID><TEXT>&amp;Make Default</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>6</INDEX_SMALL><INDEX_LARGE>6</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button</ELEMENT_NAME><ID><NAME>ID_CHANGE_PASSWORD</NAME><VALUE>32779</VALUE></ID><TEXT>&amp;Change Password</TEXT><PALETTE_TOP>FALS!
 E</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>5</INDEX_SMALL><INDEX_LARGE>5</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><ALWAYS_DESCRIPTION>FALSE</ALWAYS_DESCRIPTION></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><NAME>View</NAME><INDEX>-1</INDEX><JUSTIFY_COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_TIME_ISSUED</NAME><VALUE>32810</VALUE></ID><TEXT>&amp;Issued</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_RENEWABLE_UNTIL</NAME><VALUE>32811</VALUE></ID><TEXT>&amp;Renewable Until</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAU!
 LT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEM!
 ENT_NAME
><ID><NAME>ID_VALID_UNTIL</NAME><VALUE>32828</VALUE></ID><TEXT>&amp;Valid Until</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_ENCRYPTION_TYPE</NAME><VALUE>32826</VALUE></ID><TEXT>&amp;Encryption Type</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_SHOW_TICKET_FLAGS</NAME><VALUE>32812</VALUE></ID><TEXT>&amp;Flags</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT></ELEMENTS></PANEL><PANEL><ELEMENT_NAME>Panel</ELEMENT_NAME><NAME>Options</NAME><INDEX>-1</INDEX><JUSTIFY_!
 COLUMNS>FALSE</JUSTIFY_COLUMNS><CENTER_COLUMN_VERT>FALSE</CENTER_COLUMN_VERT><ELEMENTS><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_KILL_TIX_ONEXIT</NAME><VALUE>32785</VALUE></ID><TEXT>&amp;Destroy Tickets on Exit</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_UPPERCASE_REALM</NAME><VALUE>32787</VALUE></ID><TEXT>Allow &amp;Mixed Case Realm Name</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_AUTO_RENEW</NAME><VALUE>32808</VALUE></ID><TEXT>Automatic Ticket &amp;Renewal</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_!
 LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND><!
 /ELEMENT
><ELEMENT><ELEMENT_NAME>Button_Check</ELEMENT_NAME><ID><NAME>ID_LOW_TICKET_ALARM</NAME><VALUE>32798</VALUE></ID><TEXT>Expiration &amp;Alarm</TEXT><PALETTE_TOP>FALSE</PALETTE_TOP><ALWAYS_LARGE>FALSE</ALWAYS_LARGE><INDEX_SMALL>-1</INDEX_SMALL><INDEX_LARGE>-1</INDEX_LARGE><DEFAULT_COMMAND>TRUE</DEFAULT_COMMAND></ELEMENT></ELEMENTS></PANEL></PANELS></CATEGORY></CATEGORIES></RIBBON_BAR></AFX_RIBBON>
diff --git a/src/windows/leash/resource.h b/src/windows/leash/resource.h
index 6347318..875b844 100644
--- a/src/windows/leash/resource.h
+++ b/src/windows/leash/resource.h
@@ -334,9 +334,9 @@
 #define ID_BUTTON2                      32813
 #define ID_BUTTON5                      32816
 #define ID_BUTTON4                      32818
+#define ID_ABOUT                        32818
 #define ID_ENCRYPTION_TYPE              32826
 #define ID_VALID_UNTIL                  32828
-#define ID_BUTTON3                      32835
 #define ID_MAKE_DEFAULT                 32835
 
 // Next default values for new objects
diff --git a/src/windows/leashdll/AFSroutines.c b/src/windows/leashdll/AFSroutines.c
index 3c1dbc0..f04ab29 100644
--- a/src/windows/leashdll/AFSroutines.c
+++ b/src/windows/leashdll/AFSroutines.c
@@ -207,8 +207,7 @@ not_an_API_LeashAFSGetToken(
         list->name = strdup(aclient.name);
         list->inst = aclient.instance[0] ? strdup(aclient.instance) : NULL;
         list->realm = strdup(aclient.cell);
-        list->tktEncType = NULL;
-        list->keyEncType = NULL;
+        list->encTypes = NULL;
         list->addrCount = 0;
         list->addrList = NULL;
 
diff --git a/src/windows/leashdll/krb5routines.c b/src/windows/leashdll/krb5routines.c
index 48240bf..8de3179 100644
--- a/src/windows/leashdll/krb5routines.c
+++ b/src/windows/leashdll/krb5routines.c
@@ -315,412 +315,326 @@ one_addr(krb5_address *a)
     return(retstr);
 }
 
-/*
- * LeashKRB5GetTickets() treats krbv5Context as an in/out variable.
- * If the caller does not provide a krb5_context, one will be allocated.
- * It is up to the caller to ensure that the context is eventually freed.
- * A context can be returned even if the function returns an error.
- */
+static void
+CredToTicketInfo(krb5_creds KRBv5Credentials, TICKETINFO *ticketinfo)
+{
+    ticketinfo->issued = KRBv5Credentials.times.starttime;
+    ticketinfo->valid_until = KRBv5Credentials.times.endtime;
+    ticketinfo->renew_until =
+        KRBv5Credentials.ticket_flags & TKT_FLG_RENEWABLE ?
+        KRBv5Credentials.times.renew_till : 0;
+    _tzset();
+    if (ticketinfo->valid_until - time(0) <= 0L)
+        ticketinfo->btickets = EXPD_TICKETS;
+    else
+        ticketinfo->btickets = GOOD_TICKETS;
+}
 
-long
-not_an_API_LeashKRB5GetTickets(
-    TICKETINFO * ticketinfo,
-    TicketList** ticketList,
-    krb5_context *krbv5Context
-    )
+static int
+CredToTicketList(krb5_context ctx, krb5_creds KRBv5Credentials,
+                 char *PrincipalName, TicketList ***ticketListTail)
 {
-#ifdef NO_KRB5
-    return(0);
-#else
-    krb5_context	ctx = 0;
-    krb5_ccache		cache = 0;
-    krb5_error_code	code;
-    krb5_principal	KRBv5Principal;
-    krb5_flags		flags = 0;
-    krb5_cc_cursor	KRBv5Cursor;
-    krb5_creds		KRBv5Credentials;
-    krb5_ticket    *tkt=NULL;
-    int				StartMonth;
-    int				EndMonth;
-    int             RenewMonth;
-    int				StartDay;
-    int				EndDay;
-    int             RenewDay;
-    char			StartTimeString[256];
-    char			EndTimeString[256];
-    char            RenewTimeString[256];
-    char			fill;
-    char			*ClientName;
-    char			*PrincipalName;
-    char			*sServerName;
-    char			Buffer[256];
-    char			Months[12][4] = {"Jan\0", "Feb\0", "Mar\0", "Apr\0", "May\0", "Jun\0", "Jul\0", "Aug\0", "Sep\0", "Oct\0", "Nov\0", "Dec\0"};
-    char			StartTime[16];
-    char			EndTime[16];
-    char            RenewTime[16];
-    char			temp[128];
-    char			*sPtr;
-    char            *ticketFlag;
-    LPCSTR          functionName;
-    TicketList         *list = NULL;
-
-    if ( ticketinfo ) {
-        ticketinfo->btickets = NO_TICKETS;
-        ticketinfo->principal[0] = '\0';
-    }
+    krb5_error_code code = 0;
+    krb5_ticket *tkt=NULL;
+    char *sServerName = NULL;
+    char Buffer[256];
+    char *ticketFlag;
+    char *functionName = NULL;
+    TicketList *list = NULL;
+
+    functionName = "krb5_unparse_name()";
+    code = (*pkrb5_unparse_name)(ctx, KRBv5Credentials.server, &sServerName);
+    if (code)
+        goto cleanup;
 
-    if ((code = Leash_krb5_initialize(&(*krbv5Context), &cache)))
-        return(code);
+    if (!KRBv5Credentials.times.starttime)
+        KRBv5Credentials.times.starttime = KRBv5Credentials.times.authtime;
 
-    ctx = (*krbv5Context);
+    memset(Buffer, '\0', sizeof(Buffer));
 
-#ifdef KRB5_TC_NOTICKET
-    flags = KRB5_TC_NOTICKET;
-#endif
-    if ((code = pkrb5_cc_set_flags(ctx, cache, flags)))
-    {
-        if (code != KRB5_FCC_NOFILE && code != KRB5_CC_NOTFOUND)
-            Leash_krb5_error(code, "krb5_cc_set_flags()", 0, &ctx,
-                                  &cache);
-        else if ((code == KRB5_FCC_NOFILE || code == KRB5_CC_NOTFOUND))
-        {
-            if (cache != NULL)
-                pkrb5_cc_close(ctx, cache);
-        }
-        return code;
-    }
+    ticketFlag = GetTicketFlag(&KRBv5Credentials);
 
-    if ((code = pkrb5_cc_get_principal(ctx, cache, &KRBv5Principal)))
-    {
-        if (code != KRB5_FCC_NOFILE && code != KRB5_CC_NOTFOUND)
-            Leash_krb5_error(code, "krb5_cc_get_principal()", 0, &ctx, &cache);
-        else if ((code == KRB5_FCC_NOFILE || code == KRB5_CC_NOTFOUND))
-        {
-            if (cache != NULL)
-                pkrb5_cc_close(ctx, cache);
-        }
-        return code;
+    // @fixme: calloc for ptr init
+    list = calloc(1, sizeof(TicketList));
+    if (list == NULL) {
+        code = ENOMEM;
+        functionName = "calloc()";
+        goto cleanup;
     }
-
-    PrincipalName = NULL;
-    ClientName = NULL;
-    sServerName = NULL;
-    if ((code = (*pkrb5_unparse_name)(ctx, KRBv5Principal,
-                                      (char **)&PrincipalName)))
-    {
-        if (PrincipalName != NULL)
-            (*pkrb5_free_unparsed_name)(ctx, PrincipalName);
-
-        (*pkrb5_free_principal)(ctx, KRBv5Principal);
-        if (ctx != NULL)
-        {
-            if (cache != NULL)
-                pkrb5_cc_close(ctx, cache);
-        }
-
-        return(code);
+    list->service = strdup(sServerName);
+    if (!list->service) {
+        code = ENOMEM;
+        functionName = "calloc()";
+        goto cleanup;
     }
-
-    if (!strcspn(PrincipalName, "@" ))
-    {
-        if (PrincipalName != NULL)
-            (*pkrb5_free_unparsed_name)(ctx, PrincipalName);
-
-        (*pkrb5_free_principal)(ctx, KRBv5Principal);
-        if (ctx != NULL)
-        {
-            if (cache != NULL)
-                pkrb5_cc_close(ctx, cache);
-        }
-
-        return(code);
+    list->issued = KRBv5Credentials.times.starttime;
+    list->valid_until = KRBv5Credentials.times.endtime;
+    if (KRBv5Credentials.ticket_flags & TKT_FLG_RENEWABLE)
+        list->renew_until = KRBv5Credentials.times.renew_till;
+    else
+        list->renew_until = 0;
+
+    if (!pkrb5_decode_ticket(&KRBv5Credentials.ticket, &tkt)) {
+        wsprintf(Buffer, "Session Key: %s  Ticket: %s",
+                 etype_string(KRBv5Credentials.keyblock.enctype),
+                 etype_string(tkt->enc_part.enctype));
+        pkrb5_free_ticket(ctx, tkt);
+        tkt = NULL;
+    } else {
+        wsprintf(Buffer, "Session Key: %s",
+                 etype_string(KRBv5Credentials.keyblock.enctype));
     }
 
-    if ( strcmp(ticketinfo->principal, PrincipalName) )
-        wsprintf(ticketinfo->principal, "%s", PrincipalName);
-
-    (*pkrb5_free_principal)(ctx, KRBv5Principal);
-    if ((code = pkrb5_cc_start_seq_get(ctx, cache, &KRBv5Cursor)))
-    {
-        functionName = "krb5_cc_start_seq_get()";
-        goto on_error;
+    list->encTypes = calloc(1, strlen(Buffer)+1);
+    if (list->encTypes == NULL) {
+        functionName = "calloc()";
+        code = ENOMEM;
+        goto cleanup;
     }
+    strcpy(list->encTypes, Buffer);
 
-    memset(&KRBv5Credentials, '\0', sizeof(KRBv5Credentials));
-
-    while (!(code = pkrb5_cc_next_cred(ctx, cache, &KRBv5Cursor, &KRBv5Credentials)))
-    {
-        if ((*pkrb5_is_config_principal)(ctx, KRBv5Credentials.server))
-        { /* skip configuration credentials */
-            (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);
-            continue;
-        }
-        if (!list)
-        {
-            list = (TicketList*) calloc(1, sizeof(TicketList));
-            (*ticketList) = list;
-        }
-        else
-        {
-            list->next = (struct TicketList*) calloc(1, sizeof(TicketList));
-            list = (TicketList*) list->next;
+cleanup:
+    if (code) {
+        Leash_krb5_error(code, functionName, 0, &ctx, NULL);
+        if (list != NULL) {
+            not_an_API_LeashFreeTicketList(&list);
         }
+    } else {
+        **ticketListTail = list;
+        *ticketListTail = &list->next;
+    }
 
-        if ((*pkrb5_unparse_name)(ctx, KRBv5Credentials.client, &ClientName))
-        {
-            (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);
-            Leash_krb5_error(code, "krb5_free_cred_contents()", 0, &ctx, &cache);
-
-            if (ClientName != NULL)
-                (*pkrb5_free_unparsed_name)(ctx, ClientName);
+    if (sServerName != NULL)
+        (*pkrb5_free_unparsed_name)(ctx, sServerName);
 
-            ClientName = NULL;
-            sServerName = NULL;
-            continue;
-        }
+    return code;
+}
 
-        if ((*pkrb5_unparse_name)(ctx, KRBv5Credentials.server, &sServerName))
-        {
-            (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);
-            Leash_krb5_error(code, "krb5_free_cred_contents()", 0, &ctx, &cache);
+int
+do_ccache(krb5_context ctx,
+          krb5_ccache cache,
+          TICKETINFO ***ticketInfoTail)
+{
+    krb5_cc_cursor cur;
+    krb5_creds creds;
+    krb5_principal princ = NULL;
+    krb5_flags flags;
+    krb5_error_code code;
+    char *defname = NULL;
+    char *functionName = NULL;
+    TicketList **ticketListTail;
+    TICKETINFO *ticketinfo;
 
-            if (ClientName != NULL)
-                (*pkrb5_free_unparsed_name)(ctx, ClientName);
+    flags = 0;                          /* turns off OPENCLOSE mode */
+    code = pkrb5_cc_set_flags(ctx, cache, flags);
+    if (code) {
+        functionName = "krb5_cc_set_flags";
+        goto cleanup;
+    }
+    code = pkrb5_cc_get_principal(ctx, cache, &princ);
+    if (code) {
+        functionName = "krb5_cc_get_principal";
+        goto cleanup;
+    }
+    code = pkrb5_unparse_name(ctx, princ, &defname);
+    if (code) {
+        functionName = "krb5_unparse_name";
+        goto cleanup;
+    }
+    code = pkrb5_cc_start_seq_get(ctx, cache, &cur);
+    if (code) {
+        functionName = "krb5_cc_start_seq_get";
+        goto cleanup;
+    }
 
-            ClientName = NULL;
-            sServerName = NULL;
+    ticketinfo = calloc(1, sizeof(TICKETINFO));
+    if (ticketinfo == NULL) {
+        functionName = "calloc";
+        code = ENOMEM;
+        goto cleanup;
+    }
+    ticketinfo->next = NULL;
+    ticketinfo->ticket_list = NULL;
+    ticketinfo->principal = strdup(defname);
+    if (ticketinfo->principal == NULL) {
+        functionName = "strdup";
+        code = ENOMEM;
+        goto cleanup;
+    }
+    ticketinfo->ccache_name = strdup(pkrb5_cc_get_name(ctx, cache));
+    if (ticketinfo->ccache_name == NULL) {
+        functionName = "strdup";
+        code = ENOMEM;
+        goto cleanup;
+    }
+    **ticketInfoTail = ticketinfo;
+    *ticketInfoTail = &ticketinfo->next;
+    ticketListTail = &ticketinfo->ticket_list;
+    while (!(code = pkrb5_cc_next_cred(ctx, cache, &cur, &creds))) {
+        if (pkrb5_is_config_principal(ctx, creds.server))
             continue;
+        CredToTicketList(ctx, creds, defname, &ticketListTail);
+        CredToTicketInfo(creds, ticketinfo);
+        pkrb5_free_cred_contents(ctx, &creds);
+    }
+    if (code == KRB5_CC_END) {
+        code = pkrb5_cc_end_seq_get(ctx, cache, &cur);
+        if (code) {
+            functionName = "krb5_cc_end_seq_get";
+            goto cleanup;
         }
-
-        if (!KRBv5Credentials.times.starttime)
-            KRBv5Credentials.times.starttime = KRBv5Credentials.times.authtime;
-
-        fill = ' ';
-        memset(StartTimeString, '\0', sizeof(StartTimeString));
-        memset(EndTimeString, '\0', sizeof(EndTimeString));
-        memset(RenewTimeString, '\0', sizeof(RenewTimeString));
-        (*pkrb5_timestamp_to_sfstring)((krb5_timestamp)KRBv5Credentials.times.starttime, StartTimeString, 17, &fill);
-        (*pkrb5_timestamp_to_sfstring)((krb5_timestamp)KRBv5Credentials.times.endtime, EndTimeString, 17, &fill);
-		if (KRBv5Credentials.times.renew_till >= 0)
-			(*pkrb5_timestamp_to_sfstring)((krb5_timestamp)KRBv5Credentials.times.renew_till, RenewTimeString, 17, &fill);
-        memset(temp, '\0', sizeof(temp));
-        memcpy(temp, StartTimeString, 2);
-        StartDay = atoi(temp);
-        memset(temp, (int)'\0', (size_t)sizeof(temp));
-        memcpy(temp, EndTimeString, 2);
-        EndDay = atoi(temp);
-        memset(temp, (int)'\0', (size_t)sizeof(temp));
-        memcpy(temp, RenewTimeString, 2);
-        RenewDay = atoi(temp);
-
-        memset(temp, '\0', sizeof(temp));
-        memcpy(temp, &StartTimeString[3], 2);
-        StartMonth = atoi(temp);
-        memset(temp, '\0', sizeof(temp));
-        memcpy(temp, &EndTimeString[3], 2);
-        EndMonth = atoi(temp);
-        memset(temp, '\0', sizeof(temp));
-        memcpy(temp, &RenewTimeString[3], 2);
-        RenewMonth = atoi(temp);
-
-        while (1)
-        {
-            if ((sPtr = strrchr(StartTimeString, ' ')) == NULL)
-                break;
-
-            if (strlen(sPtr) != 1)
-                break;
-
-            (*sPtr) = 0;
+        flags = KRB5_TC_OPENCLOSE;      /* turns on OPENCLOSE mode */
+        code = pkrb5_cc_set_flags(ctx, cache, flags);
+        if (code) {
+            functionName = "krb5_cc_set_flags";
+            goto cleanup;
         }
+    } else {
+        functionName = "krb5_cc_next_cred";
+        goto cleanup;
+    }
+cleanup:
+    if (code) {
+        Leash_krb5_error(code, functionName, 0, NULL, NULL);
+    }
+    if (princ)
+        pkrb5_free_principal(ctx, princ);
+    if (defname)
+        pkrb5_free_unparsed_name(ctx, defname);
+    return code ? 1 : 0;
+}
 
-        while (1)
-        {
-            if ((sPtr = strrchr(EndTimeString, ' ')) == NULL)
-                break;
-
-            if (strlen(sPtr) != 1)
-                break;
 
-            (*sPtr) = 0;
-        }
+//
+// Returns 0 for success, 1 for failure
+//
+int
+do_all_ccaches(krb5_context ctx, TICKETINFO **ticketinfotail)
+{
+    krb5_error_code code;
+    krb5_ccache cache;
+    krb5_cccol_cursor cursor;
+    int retval = 0;
+    char *functionName = NULL;
 
-        while (1)
-        {
-            if ((sPtr = strrchr(RenewTimeString, ' ')) == NULL)
-                break;
+    code = pkrb5_cccol_cursor_new(ctx, &cursor);
+    if (code) {
+        functionName = "krb5_cccol_cursor_new";
+        goto cleanup;
+    }
+    retval = 0;
+    while (!(code = pkrb5_cccol_cursor_next(ctx, cursor, &cache)) &&
+           cache != NULL) {
+        // Note that ticketList will be updated here to point to the tail
+        // of the list but the caller of this function will remain with a
+        // pointer to the head.
+        do_ccache(ctx, cache, &ticketinfotail);
+        pkrb5_cc_close(ctx, cache);
+    }
+    if (code)
+         functionName = "krb5_cccol_cursor_next";
+    pkrb5_cccol_cursor_free(ctx, &cursor);
+cleanup:
+    if (code) {
+        Leash_krb5_error(code, functionName, 0, NULL, NULL);
+    }
+    return retval;
+}
 
-            if (strlen(sPtr) != 1)
-                break;
+static void FreeTicketInfo(TICKETINFO *ticketinfo)
+{
+    if (ticketinfo->principal) {
+        free(ticketinfo->principal);
+        ticketinfo->principal = NULL;
+    }
+    if (ticketinfo->ccache_name) {
+        free(ticketinfo->ccache_name);
+        ticketinfo->ccache_name = NULL;
+    }
+    if (ticketinfo->ticket_list)
+        not_an_API_LeashFreeTicketList(&ticketinfo->ticket_list);
+}
 
-            (*sPtr) = 0;
+long
+not_an_API_LeashKRB5FreeTickets(TICKETINFO *ticketinfo)
+{
+    TICKETINFO *initial = ticketinfo; // @TEMP fixme
+    TICKETINFO *next;
+    while (ticketinfo != NULL) {
+        next = ticketinfo->next;
+        FreeTicketInfo(ticketinfo);
+        // @TEMP fixme
+        if (ticketinfo != initial) {
+            free(ticketinfo);
         }
+        ticketinfo = next;
+    }
+    return 0;
+}
 
-        memset(StartTime, '\0', sizeof(StartTime));
-        memcpy(StartTime, &StartTimeString[strlen(StartTimeString) - 5], 5);
-        memset(EndTime, '\0', sizeof(EndTime));
-        memcpy(EndTime, &EndTimeString[strlen(EndTimeString) - 5], 5);
-        memset(RenewTime, '\0', sizeof(RenewTime));
-        memcpy(RenewTime, &RenewTimeString[strlen(RenewTimeString) - 5], 5);
-
-        memset(temp, '\0', sizeof(temp));
-        strcpy(temp, ClientName);
 
-        if (!strcmp(ClientName, PrincipalName))
-            memset(temp, '\0', sizeof(temp));
+/*
+ * LeashKRB5GetTickets() treats krbv5Context as an in/out variable.
+ * If the caller does not provide a krb5_context, one will be allocated.
+ * It is up to the caller to ensure that the context is eventually freed.
+ * A context can be returned even if the function returns an error.
+ */
 
-        memset(Buffer, '\0', sizeof(Buffer));
+long
+not_an_API_LeashKRB5GetTickets(TICKETINFO *ticketinfo,
+                               krb5_context *krbv5Context)
+{
+    krb5_error_code code;
+    krb5_principal me = 0;
+    krb5_context ctx = 0;
+    krb5_ccache cache = 0;
+    char *PrincipalName = NULL;
 
-        ticketFlag = GetTicketFlag(&KRBv5Credentials);
+    code = Leash_krb5_initialize(krbv5Context);
+    if (code)
+        return code;
 
-        if (KRBv5Credentials.ticket_flags & TKT_FLG_RENEWABLE) {
-            wsprintf(Buffer,"%s %02d %s     %s %02d %s     [%s %02d %s]     %s %s       %s",
-                      Months[StartMonth - 1], StartDay, StartTime,
-                      Months[EndMonth - 1], EndDay, EndTime,
-                      Months[RenewMonth - 1], RenewDay, RenewTime,
-                      sServerName,
-                      temp, ticketFlag);
-        } else {
-            wsprintf(Buffer,"%s %02d %s     %s %02d %s     %s %s       %s",
-                 Months[StartMonth - 1], StartDay, StartTime,
-                 Months[EndMonth - 1], EndDay, EndTime,
-                 sServerName,
-                 temp, ticketFlag);
-        }
-        list->theTicket = (char*) calloc(1, strlen(Buffer)+1);
-        if (!list->theTicket)
-        {
-#ifdef USE_MESSAGE_BOX
-            MessageBox(NULL, "Memory Error", "Error", MB_OK);
-#endif /* USE_MESSAGE_BOX */
-            return ENOMEM;
-        }
-        strcpy(list->theTicket, Buffer);
-        list->name = NULL;
-        list->inst = NULL;
-        list->realm = NULL;
-
-        if ( !pkrb5_decode_ticket(&KRBv5Credentials.ticket, &tkt)) {
-            wsprintf(Buffer, "Ticket Encryption Type: %s", etype_string(tkt->enc_part.enctype));
-            list->tktEncType = (char*) calloc(1, strlen(Buffer)+1);
-            if (!list->tktEncType)
-            {
-#ifdef USE_MESSAGE_BOX
-                MessageBox(NULL, "Memory Error", "Error", MB_OK);
-#endif /* USE_MESSAGE_BOX */
-                return ENOMEM;
-            }
-            strcpy(list->tktEncType, Buffer);
+    ctx = *krbv5Context;
 
-            pkrb5_free_ticket(ctx, tkt);
-            tkt = NULL;
-        } else {
-            list->tktEncType = NULL;
-        }
+    // @TEMP fixme; shouldn't be necessary
+    // save default principal name in ticketinfo
+    if (ticketinfo != NULL) {
+        ticketinfo->btickets = NO_TICKETS;
+        ticketinfo->principal = NULL;
+        ticketinfo->ccache_name = NULL;
+        ticketinfo->next = NULL;
+        ticketinfo->ticket_list = NULL;
 
-        wsprintf(Buffer, "Session Key Type: %s", etype_string(KRBv5Credentials.keyblock.enctype));
-        list->keyEncType = (char*) calloc(1, strlen(Buffer)+1);
-        if (!list->keyEncType)
-        {
-#ifdef USE_MESSAGE_BOX
-            MessageBox(NULL, "Memory Error", "Error", MB_OK);
-#endif /* USE_MESSAGE_BOX */
-            return ENOMEM;
+        code = pkrb5_cc_default(ctx, &cache);
+        if (code)
+            goto cleanup;
+        ticketinfo->ccache_name = strdup(pkrb5_cc_get_name(ctx, cache));
+        if (ticketinfo->ccache_name == NULL) {
+            code = ENOMEM;
+            goto cleanup;
         }
-        strcpy(list->keyEncType, Buffer);
-
-        if ( KRBv5Credentials.addresses && KRBv5Credentials.addresses[0] ) {
-            int n = 0;
-            while ( KRBv5Credentials.addresses[n] )
-				n++;
-            list->addrList = calloc(1, n * sizeof(char *));
-            if (!list->addrList) {
-#ifdef USE_MESSAGE_BOX
-                MessageBox(NULL, "Memory Error", "Error", MB_OK);
-#endif /* USE_MESSAGE_BOX */
-                return ENOMEM;
+        if (!pkrb5_cc_get_principal(ctx, cache, &me)) {
+            code = (*pkrb5_unparse_name)(ctx, me, &PrincipalName);
+            if (code)
+                goto cleanup;
+            if (PrincipalName) {
+                ticketinfo->principal = strdup(PrincipalName);
+                pkrb5_free_unparsed_name(ctx, PrincipalName);
             }
-            list->addrCount = n;
-            for ( n=0; n<list->addrCount; n++ ) {
-                wsprintf(Buffer, "Address: %s", one_addr(KRBv5Credentials.addresses[n]));
-                list->addrList[n] = (char*) calloc(1, strlen(Buffer)+1);
-                if (!list->addrList[n])
-                {
-#ifdef USE_MESSAGE_BOX
-                    MessageBox(NULL, "Memory Error", "Error", MB_OK);
-#endif /* USE_MESSAGE_BOX */
-                    return ENOMEM;
-                }
-                strcpy(list->addrList[n], Buffer);
-            }
-        }
-
-        ticketinfo->issue_date = KRBv5Credentials.times.starttime;
-        ticketinfo->lifetime = KRBv5Credentials.times.endtime - KRBv5Credentials.times.starttime;
-        ticketinfo->renew_till = KRBv5Credentials.ticket_flags & TKT_FLG_RENEWABLE ?
-            KRBv5Credentials.times.renew_till : 0;
-        _tzset();
-        if ( ticketinfo->issue_date + ticketinfo->lifetime - time(0) <= 0L )
-            ticketinfo->btickets = EXPD_TICKETS;
-        else
-            ticketinfo->btickets = GOOD_TICKETS;
-
-	if (ClientName != NULL)
-            (*pkrb5_free_unparsed_name)(ctx, ClientName);
-
-        if (sServerName != NULL)
-            (*pkrb5_free_unparsed_name)(ctx, sServerName);
-
-        ClientName = NULL;
-        sServerName = NULL;
-        (*pkrb5_free_cred_contents)(ctx, &KRBv5Credentials);
-    }
-
-    if (PrincipalName != NULL)
-        (*pkrb5_free_unparsed_name)(ctx, PrincipalName);
-
-    if (ClientName != NULL)
-        (*pkrb5_free_unparsed_name)(ctx, ClientName);
-
-    if (sServerName != NULL)
-        (*pkrb5_free_unparsed_name)(ctx, sServerName);
-
-    if ((code == KRB5_CC_END) || (code == KRB5_CC_NOTFOUND))
-    {
-        if ((code = pkrb5_cc_end_seq_get(ctx, cache, &KRBv5Cursor)))
-        {
-            functionName = "krb5_cc_end_seq_get()";
-            goto on_error;
         }
-
-        flags = KRB5_TC_OPENCLOSE;
-#ifdef KRB5_TC_NOTICKET
-        flags |= KRB5_TC_NOTICKET;
-#endif
-        if ((code = pkrb5_cc_set_flags(ctx, cache, flags)))
-        {
-            functionName = "krb5_cc_set_flags()";
-            goto on_error;
-        }
-    }
-    else
-    {
-        functionName = "krb5_cc_next_cred()";
-        goto on_error;
-    }
-
-    if (ctx != NULL)
-    {
-        if (cache != NULL)
-            pkrb5_cc_close(ctx, cache);
     }
 
-    return(code);
+    do_all_ccaches(*krbv5Context, &ticketinfo->next);
+    // @TEMP aggregate ticket info here?
 
- on_error:
-    Leash_krb5_error(code, functionName, 0, &(*krbv5Context), &cache);
-    return(code);
-#endif //!NO_KER5
+cleanup:
+    if (code)
+        not_an_API_LeashKRB5FreeTickets(ticketinfo);
+    if (cache)
+        pkrb5_cc_close(ctx, cache);
+    if (me)
+        pkrb5_free_principal(ctx, me);
+    return code;
 }
 
 
@@ -831,13 +745,15 @@ DWORD                       publicIP
 #else
     krb5_error_code		        code = 0;
     krb5_context		        ctx = 0;
-    krb5_ccache			        cc = 0;
+    krb5_ccache			        cc = 0, defcache = 0;
     krb5_principal		        me = 0;
     char*                       name = 0;
     krb5_creds			        my_creds;
     krb5_get_init_creds_opt *   options = NULL;
     krb5_address **             addrs = NULL;
     int                         i = 0, addr_count = 0;
+    int                         cc_new = 0;
+    const char *                deftype = NULL;
 
     if (!pkrb5_init_context)
         return 0;
@@ -857,12 +773,29 @@ DWORD                       publicIP
     code = pkrb5_get_init_creds_opt_alloc(ctx, &options);
     if (code) goto cleanup;
 
-    code = pkrb5_cc_default(ctx, &cc);
+    code = pkrb5_cc_default(ctx, &defcache);
     if (code) goto cleanup;
 
     code = pkrb5_parse_name(ctx, principal_name, &me);
     if (code) goto cleanup;
 
+    deftype = pkrb5_cc_get_type(ctx, defcache);
+    if (me != NULL && pkrb5_cc_support_switch(ctx, deftype)) {
+        /* Use an existing cache for the specified principal if we can. */
+        code = pkrb5_cc_cache_match(ctx, me, &cc);
+        if (code != 0 && code != KRB5_CC_NOTFOUND)
+            goto cleanup;
+        if (code == KRB5_CC_NOTFOUND) {
+            code = pkrb5_cc_new_unique(ctx, deftype, NULL, &cc);
+            if (code)
+                goto cleanup;
+            cc_new = 1;
+        }
+        pkrb5_cc_close(ctx, defcache);
+    } else {
+        cc = defcache;
+    }
+
     code = pkrb5_unparse_name(ctx, me, &name);
     if (code) goto cleanup;
 
@@ -957,7 +890,24 @@ DWORD                       publicIP
                                        0, // start time
                                        0, // service name
                                        options);
+    // @TODO: make this an option
+    if ((!code) && (cc != defcache)) {
+        code = pkrb5_cc_switch(ctx, cc);
+        if (!code) {
+            const char *cctype = pkrb5_cc_get_type(ctx, cc);
+            if (cctype != NULL) {
+                char defname[20];
+                sprintf_s(defname, sizeof(defname), "%s:", cctype);
+                pkrb5int_cc_user_set_default_name(ctx, defname);
+            }
+        }
+    }
  cleanup:
+    if (code && cc_new) {
+        // don't leave newly-generated empty ccache lying around on failure
+        pkrb5_cc_destroy(ctx, cc);
+        cc = NULL;
+    }
     if ( addrs ) {
         for ( i=0;i<addr_count;i++ ) {
             if ( addrs[i] ) {
@@ -1002,7 +952,11 @@ Leash_krb5_kdestroy(
 
     ctx = NULL;
     cache = NULL;
-    if (rc = Leash_krb5_initialize(&ctx, &cache))
+    rc = Leash_krb5_initialize(&ctx);
+    if (rc)
+        return(rc);
+
+    if (rc = pkrb5_cc_default(ctx, &cache))
         return(rc);
 
     rc = pkrb5_cc_destroy(ctx, cache);
@@ -1015,56 +969,62 @@ Leash_krb5_kdestroy(
 #endif //!NO_KRB5
 }
 
+krb5_error_code
+Leash_krb5_cc_default(krb5_context *ctx, krb5_ccache *cache)
+{
+    krb5_error_code rc;
+    krb5_flags flags;
+
+    char *functionName = NULL;
+    if (*cache == 0) {
+        rc = pkrb5_cc_default(*ctx, cache);
+        if (rc) {
+            functionName = "krb5_cc_default()";
+            goto on_error;
+        }
+    }
+#ifdef KRB5_TC_NOTICKET
+    flags = KRB5_TC_NOTICKET;
+#endif
+    rc = pkrb5_cc_set_flags(*ctx, *cache, flags);
+    if (rc) {
+        if (rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) {
+            if (*cache != NULL && *ctx != NULL)
+                pkrb5_cc_close(*ctx, *cache);
+        } else {
+            functionName = "krb5_cc_set_flags()";
+            goto on_error;
+        }
+    }
+on_error:
+    if (rc && functionName) {
+        Leash_krb5_error(rc, functionName, 0, ctx, cache);
+    }
+    return rc;
+}
+
 /**************************************/
 /* Leash_krb5_initialize():             */
 /**************************************/
-int Leash_krb5_initialize(krb5_context *ctx, krb5_ccache *cache)
+int Leash_krb5_initialize(krb5_context *ctx)
 {
 #ifdef NO_KRB5
     return(0);
 #else
 
     LPCSTR          functionName = NULL;
-    int             freeContextFlag = 0;
     krb5_error_code	rc;
-    krb5_flags          flags;
 
     if (pkrb5_init_context == NULL)
         return 1;
 
     if (*ctx == 0) {
-        if (rc = (*pkrb5_init_context)(ctx))
-    {
-        functionName = "krb5_init_context()";
-        goto on_error;
-    }
-        freeContextFlag = 1;
-    }
-
-    if (*cache == 0 && (rc = pkrb5_cc_default(*ctx, cache)))
-    {
-        functionName = "krb5_cc_default()";
-        goto on_error;
-    }
-#ifdef KRB5_TC_NOTICKET
-    flags = KRB5_TC_NOTICKET;
-#endif
-    if ((rc = pkrb5_cc_set_flags(*ctx, *cache, flags)))
-    {
-        if (rc != KRB5_FCC_NOFILE && rc != KRB5_CC_NOTFOUND)
-            Leash_krb5_error(rc, "krb5_cc_set_flags()", 0, ctx,
-                                  cache);
-        else if ((rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) && *ctx != NULL)
-        {
-            if (*cache != NULL)
-                pkrb5_cc_close(*ctx, *cache);
+        if (rc = (*pkrb5_init_context)(ctx)) {
+            functionName = "krb5_init_context()";
+            return Leash_krb5_error(rc, functionName, 0, ctx, NULL);
         }
-        return rc;
     }
-	return 0;
-
-  on_error:
-    return Leash_krb5_error(rc, functionName, freeContextFlag, ctx, cache);
+    return 0;
 #endif //!NO_KRB5
 }
 
@@ -1083,36 +1043,25 @@ Leash_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
 #ifdef USE_MESSAGE_BOX
     char message[256];
     const char *errText;
-    int krb5Error = ((int)(rc & 255));
-
-    /*
-    switch (krb5Error)
-    {
-        // Wrong password
-        case 31:
-        case 8:
-            return;
-    }
-    */
 
     errText = perror_message(rc);
     _snprintf(message, sizeof(message),
               "%s\n(Kerberos error %ld)\n\n%s failed",
               errText,
-              krb5Error,
+              rc,
               FailedFunctionName);
+    message[sizeof(message)-1] = 0;
 
     MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
                MB_TASKMODAL |
                MB_SETFOREGROUND);
 #endif /* USE_MESSAGE_BOX */
 
-        if (*ctx != NULL)
-        {
-            if (*cache != NULL) {
-                pkrb5_cc_close(*ctx, *cache);
-                *cache = NULL;
-            }
+    if (ctx != NULL && *ctx != NULL) {
+        if (cache != NULL && *cache != NULL) {
+            pkrb5_cc_close(*ctx, *cache);
+            *cache = NULL;
+        }
 
         if (FreeContextFlag) {
             pkrb5_free_context(*ctx);
@@ -1139,7 +1088,6 @@ Leash_ms2mit(BOOL save_creds)
     krb5_creds creds;
     krb5_cc_cursor cursor=0;
     krb5_principal princ = 0;
-    char *cache_name=NULL;
     BOOL rc = FALSE;
 
     if ( !pkrb5_init_context )
diff --git a/src/windows/leashdll/leash-int.h b/src/windows/leashdll/leash-int.h
index fb7617e..b5c0b27 100644
--- a/src/windows/leashdll/leash-int.h
+++ b/src/windows/leashdll/leash-int.h
@@ -162,7 +162,9 @@ BOOL IsKerberosLogon(VOID);
 int Leash_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
                      int FreeContextFlag, krb5_context *ctx,
                      krb5_ccache *cache);
-int Leash_krb5_initialize(krb5_context *, krb5_ccache *);
+int Leash_krb5_initialize(krb5_context *);
+krb5_error_code
+Leash_krb5_cc_default(krb5_context *ctx, krb5_ccache *cache);
 #endif /* NO_KRB5 */
 
 LPSTR err_describe(LPSTR buf, long code);
diff --git a/src/windows/leashdll/leashdll.h b/src/windows/leashdll/leashdll.h
index a045583d..63cfe23 100644
--- a/src/windows/leashdll/leashdll.h
+++ b/src/windows/leashdll/leashdll.h
@@ -58,6 +58,8 @@ void FAR Leash_load_com_err_callback(FARPROC,FARPROC,FARPROC);
 #endif
 #include <ntsecapi.h>
 
+#include <krb5.h>
+
 #ifndef NO_KRB4
 extern HINSTANCE hKrb4;
 #endif
@@ -73,19 +75,6 @@ extern HINSTANCE hProfile;
 #define LEASH_PRIORITY_LOW  0
 #define LEASH_PRIORITY_HIGH 1
 
-typedef struct TicketList
-{
-    char* theTicket;
-    struct TicketList* next;
-    char* tktEncType;
-    char* keyEncType;
-    int   addrCount;
-    char ** addrList;
-    char * name;
-    char * inst;
-    char * realm;
-} TicketList;
-
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef _WIN64
diff --git a/src/windows/leashdll/leashw32.def b/src/windows/leashdll/leashw32.def
index 25af0e2..5df8309 100644
--- a/src/windows/leashdll/leashw32.def
+++ b/src/windows/leashdll/leashw32.def
@@ -106,3 +106,4 @@ EXPORTS
 	not_an_API_LeashKRB4GetTickets
 	not_an_API_LeashGetTimeServerName
     not_an_API_Leash_AcquireInitialTicketsIfNeeded
+    not_an_API_LeashKRB5FreeTickets
diff --git a/src/windows/leashdll/lshfunc.c b/src/windows/leashdll/lshfunc.c
index 1a0bf14..614bb79 100644
--- a/src/windows/leashdll/lshfunc.c
+++ b/src/windows/leashdll/lshfunc.c
@@ -867,26 +867,9 @@ not_an_API_LeashFreeTicketList(TicketList** ticketList)
         killList = tempList;
 
         tempList = (TicketList*)tempList->next;
-        free(killList->theTicket);
-        if (killList->tktEncType)
-            free(killList->tktEncType);
-        if (killList->keyEncType)
-            free(killList->keyEncType);
-        if (killList->addrCount) {
-            int n;
-            for ( n=0; n<killList->addrCount; n++) {
-                if (killList->addrList[n])
-                    free(killList->addrList[n]);
-            }
-        }
-        if (killList->addrList)
-            free(killList->addrList);
-        if (killList->name)
-            free(killList->name);
-        if (killList->inst)
-            free(killList->inst);
-        if (killList->realm)
-            free(killList->realm);
+        free(killList->service);
+        if (killList->encTypes)
+            free(killList->encTypes);
         free(killList);
     }
 
@@ -2873,8 +2856,7 @@ acquire_tkt_no_princ(krb5_context context, char * ccname, int cclen)
 	GetEnvironmentVariable("KRB5CCNAME", ccachename, sizeof(ccachename));
     }
 
-    not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);
-    not_an_API_LeashFreeTicketList(&list);
+    not_an_API_LeashKRB5GetTickets(&ticketinfo,&ctx);
 
     if ( ticketinfo.btickets != GOOD_TICKETS &&
          dwMsLsaImport && Leash_importable() ) {
@@ -2939,8 +2921,8 @@ acquire_tkt_no_princ(krb5_context context, char * ccname, int cclen)
         if ( import ) {
             Leash_import();
 
-            not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);
-            not_an_API_LeashFreeTicketList(&list);
+            not_an_API_LeashKRB5FreeTickets(&ticketinfo);
+            not_an_API_LeashKRB5GetTickets(&ticketinfo,&ctx);
         }
     }
 
@@ -2958,7 +2940,7 @@ acquire_tkt_no_princ(krb5_context context, char * ccname, int cclen)
 	strncpy(ccname, ccachename, cclen);
 	ccname[cclen-1] = '\0';
     }
-
+    not_an_API_LeashKRB5FreeTickets(&ticketinfo);
     if ( !context )
         pkrb5_free_context(ctx);
 }
@@ -2968,7 +2950,6 @@ static void
 acquire_tkt_for_princ(krb5_context context, krb5_principal desiredPrincipal,
 		      char * ccname, int cclen)
 {
-    TicketList 		*list = NULL;
     TICKETINFO   	ticketinfo;
     krb5_context        ctx;
     DWORD 		dwMsLsaImport = Leash_get_default_mslsa_import();
@@ -2992,8 +2973,7 @@ acquire_tkt_for_princ(krb5_context context, krb5_principal desiredPrincipal,
 	GetEnvironmentVariable("KRB5CCNAME", ccachename, sizeof(ccachename));
     }
 
-    not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);
-    not_an_API_LeashFreeTicketList(&list);
+    not_an_API_LeashKRB5GetTickets(&ticketinfo,&ctx);
 
     pkrb5_unparse_name(ctx, desiredPrincipal, &name);
 
@@ -3032,14 +3012,14 @@ acquire_tkt_for_princ(krb5_context context, krb5_principal desiredPrincipal,
 
 	    SetEnvironmentVariable("KRB5CCNAME", ccachename);
 
-            not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);
-            not_an_API_LeashFreeTicketList(&list);
+	    not_an_API_LeashKRB5FreeTickets(&ticketinfo);
+	    not_an_API_LeashKRB5GetTickets(&ticketinfo,&ctx);
 
 	    if (ticketinfo.btickets != GOOD_TICKETS) {
 		Leash_import();
 
-		not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);
-		not_an_API_LeashFreeTicketList(&list);
+		not_an_API_LeashKRB5FreeTickets(&ticketinfo);
+		not_an_API_LeashKRB5GetTickets(&ticketinfo,&ctx);
 	    }
 	}
     }
@@ -3059,6 +3039,7 @@ acquire_tkt_for_princ(krb5_context context, krb5_principal desiredPrincipal,
 	    ccname[cclen-1] = '\0';
 	}
     }
+    not_an_API_LeashKRB5FreeTickets(&ticketinfo);
 
     if (name)
 	pkrb5_free_unparsed_name(ctx, name);


More information about the cvs-krb5 mailing list