* ms2mit.c:
authorJeffrey Altman <jaltman@secure-endpoints.com>
Fri, 18 Jul 2003 13:17:47 +0000 (13:17 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Fri, 18 Jul 2003 13:17:47 +0000 (13:17 +0000)
    Functional changes:
    (1) do not restrict ourselves to DES-CBC-CRC instead support any
        ticket with an enctype we support.  as of this date (rev 1.3)
        this includes all but RC4-MD4.
    (2) do not accept invalid tickets
    (3) when attempting to retrieve tickets do not specify either the
        enctype or cache options (if possible).  doing so will force a
        TGS request and prevent the results from being stored into the
        cache.
    (4) when the LSA cache contains a TGT which has expired Microsoft will
        not perform a new TGS request until the cache has been purged.
        Instead the expired ticket continues to be used along with its
        embedded authorization data.  When PURGE_ENABLED is defined, if the
        tickets are expired, the cache will be purged before requesting
        new tickets, else we ignore the contents of the cache and force
        a new TGS request.
    (5) when the LSA cache is empty do not abort.  On XP or 2003, use
        the SecurityLogonSessionData to determine the Realm (UserDnsDomain
        in MS-speak) and request an appropriate TGT.  On 2000, check the
        Registry for the HKCU\"Volatile Environment":"USERDNSDOMAIN"
        instead.  This will allow ms2mit to be used to repopulate the
        LSA cache.  If the current session is not Kerberos authenticated
        an appropriate error message will be generated.

    Code changes:
    (1) several memory leaks plugged
    (2) several support functions copied from the Leashw32.dll sources
    (3) get_STRING_from_registry() uses the ANSI versions of the Registry
        functions and should at a later date be converted to use the
        Unicode versions.

    Notes: an ms2mit.exe based on the Leash_import() function
    should be considered.  Leash_import() not only imports the TGT from
    the LSA but also performs the krb524 conversion and AFS token retrieval.
    Of course, that version of ms2mit.exe could not exist within the krb5
    source tree.

ticket: 1667
target_version: 1.3.1
tags: pullup
owner: jaltman@mit.edu
status: resolved

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@15696 dc483132-0cff-0310-8789-dd5450dbe970

src/windows/ms2mit/ChangeLog
src/windows/ms2mit/ms2mit.c

index 8741abbc05e09b2859ef67b14128dcc86aec8d03..1c5a9c45f82d179b7b45996b4b3a6b95d75f48a4 100644 (file)
@@ -1,21 +1,62 @@
+2003-07-16  Jeffrey Altman <jaltman@mit.edu>
+
+    * ms2mit.c: 
+
+    Functional changes:
+    (1) do not restrict ourselves to DES-CBC-CRC instead support any
+        ticket with an enctype we support.  as of this date (rev 1.3)
+        this includes all but RC4-MD4.
+    (2) do not accept invalid tickets
+    (3) when attempting to retrieve tickets do not specify either the
+        enctype or cache options (if possible).  doing so will force a 
+        TGS request and prevent the results from being stored into the 
+        cache.
+    (4) when the LSA cache contains a TGT which has expired Microsoft will 
+        not perform a new TGS request until the cache has been purged.
+        Instead the expired ticket continues to be used along with its
+        embedded authorization data.  When PURGE_ENABLED is defined, if the 
+        tickets are expired, the cache will be purged before requesting
+        new tickets, else we ignore the contents of the cache and force 
+        a new TGS request.
+    (5) when the LSA cache is empty do not abort.  On XP or 2003, use
+        the SecurityLogonSessionData to determine the Realm (UserDnsDomain
+        in MS-speak) and request an appropriate TGT.  On 2000, check the
+        Registry for the HKCU\"Volatile Environment":"USERDNSDOMAIN" 
+        instead.  This will allow ms2mit to be used to repopulate the
+        LSA cache.  If the current session is not Kerberos authenticated
+        an appropriate error message will be generated.
+
+    Code changes:
+    (1) several memory leaks plugged
+    (2) several support functions copied from the Leashw32.dll sources
+    (3) get_STRING_from_registry() uses the ANSI versions of the Registry
+        functions and should at a later date be converted to use the 
+        Unicode versions.
+
+    Notes: an ms2mit.exe based on the Leash_import() function
+    should be considered.  Leash_import() not only imports the TGT from
+    the LSA but also performs the krb524 conversion and AFS token retrieval.
+    Of course, that version of ms2mit.exe could not exist within the krb5
+    source tree.
+
 2003-06-20  Jeffrey Altman <jaltman@mit.edu>
 
-       * ms2mit.c: Windows Credentials are addressless. Do not store the
+    * ms2mit.c: Windows Credentials are addressless. Do not store the
        credentials in the MIT cache with addresses since they do not
        contain addresses in the encrypted portion of the credential.
        Instead generate a valid empty address list.
 
 2002-08-29  Ken Raeburn  <raeburn@mit.edu>
 
-       * Makefile.in: Revert $(S)=>/ change, for Windows support.
+    * Makefile.in: Revert $(S)=>/ change, for Windows support.
 
 2002-08-23  Ken Raeburn  <raeburn@mit.edu>
 
-       * Makefile.in: Change $(S)=>/ and $(U)=>.. globally.
+    * Makefile.in: Change $(S)=>/ and $(U)=>.. globally.
 
 2001-11-28  Danilo Almeida  <dalmeida@mit.edu>
 
-       * ms2mit.c: Make sure we get a des-cbc-crc session key instead of
+    * ms2mit.c: Make sure we get a des-cbc-crc session key instead of
        potentially getting whatever happens to be in the cache.  Remove
        unnecessary static variables.  Make function headers use a
        consistent format.  Rename ShowLastError() to ShowWinError() and
index c2d1d384ec2a7e774fbbb0acde80d82462c1144a..3baaf195849f4575b035c1df6f81b183ebe2ee57 100644 (file)
@@ -24,7 +24,30 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 ******************************************************************/
-
+/*
+ * Copyright (C) 2003 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
 
 #define UNICODE
 #define _UNICODE
@@ -338,68 +361,111 @@ ConcatenateUnicodeStrings(
     return ERROR_SUCCESS;
 }
 
-BOOL
-GetMSTGT(
-    HANDLE LogonHandle,
-    ULONG PackageId,
-    KERB_EXTERNAL_TICKET **ticket
+static BOOL
+get_STRING_from_registry(
+    HKEY hBaseKey,
+    char * key,
+    char * value,
+    char * outbuf,
+    DWORD  outlen
     )
 {
-    //
-    // INVARIANTS:
-    //
-    //   (FAILED(Status) || FAILED(SubStatus)) ==> error
-    //   bIsLsaError ==> LsaCallAuthenticationPackage() error
-    //
+    HKEY hKey;
+    DWORD dwCount;
+    LONG rc;
 
-    //
-    // NOTE:
-    //
-    // The updated code leaks memory, but so does the old code.  The
-    // whole program is full of leaks.  Since it's short-lived
-    // process, it is ok.
-    //
+       if (!outbuf || outlen == 0)
+               return FALSE;
 
-    BOOL bIsLsaError = FALSE;
+    rc = RegOpenKeyExA(hBaseKey, key, 0, KEY_QUERY_VALUE, &hKey);
+    if (rc)
+        return FALSE;
+
+    dwCount = outlen;
+    rc = RegQueryValueExA(hKey, value, 0, 0, (LPBYTE) outbuf, &dwCount);
+    RegCloseKey(hKey);
+
+    return rc?FALSE:TRUE;
+}
+
+static BOOL
+GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)
+{
     NTSTATUS Status = 0;
-    NTSTATUS SubStatus = 0;
+    HANDLE  TokenHandle;
+    TOKEN_STATISTICS Stats;
+    DWORD   ReqLen;
+    BOOL    Success;
 
-    UNICODE_STRING TargetPrefix;
+    if (!ppSessionData)
+        return FALSE;
+    *ppSessionData = NULL;
 
-    KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
-    PKERB_RETRIEVE_TKT_REQUEST pTicketRequest;
-    PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
-    ULONG RequestSize;
-    ULONG ResponseSize;
-    USHORT TargetSize;
+    Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );
+    if ( !Success )
+        return FALSE;
 
-    CacheRequest.MessageType = KerbRetrieveTicketMessage;
-    CacheRequest.LogonId.LowPart = 0;
-    CacheRequest.LogonId.HighPart = 0;
+    Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
+    CloseHandle( TokenHandle );
+    if ( !Success )
+        return FALSE;
 
-    pTicketResponse = NULL;
+    Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
+    if ( FAILED(Status) || !ppSessionData )
+        return FALSE;
 
-    Status = LsaCallAuthenticationPackage(
-        LogonHandle,
-        PackageId,
-        &CacheRequest,
-        sizeof(CacheRequest),
-        &pTicketResponse,
-        &ResponseSize,
-        &SubStatus
-        );
+    return TRUE;
+}
 
-    if (FAILED(Status) || FAILED(SubStatus))
-    {
-        bIsLsaError = TRUE;
-        goto cleanup;
+//
+// IsKerberosLogon() does not validate whether or not there are valid tickets in the 
+// cache.  It validates whether or not it is reasonable to assume that if we 
+// attempted to retrieve valid tickets we could do so.  Microsoft does not 
+// automatically renew expired tickets.  Therefore, the cache could contain
+// expired or invalid tickets.  Microsoft also caches the user's password 
+// and will use it to retrieve new TGTs if the cache is empty and tickets
+// are requested.
+
+static BOOL
+IsKerberosLogon(VOID)
+{
+    PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
+    BOOL    Success = FALSE;
+
+    if ( GetSecurityLogonSessionData(&pSessionData) ) {
+        if ( pSessionData->AuthenticationPackage.Buffer ) {
+            WCHAR buffer[256];
+            WCHAR *usBuffer;
+            int usLength;
+
+            Success = FALSE;
+            usBuffer = (pSessionData->AuthenticationPackage).Buffer;
+            usLength = (pSessionData->AuthenticationPackage).Length;
+            if (usLength < 256)
+            {
+                lstrcpyn (buffer, usBuffer, usLength);
+                lstrcat (buffer,L"");
+                if ( !lstrcmp(L"Kerberos",buffer) )
+                    Success = TRUE;
+            }
+        }
+        LsaFreeReturnBuffer(pSessionData);
     }
+    return Success;
+}
 
-    if (pTicketResponse->Ticket.SessionKey.KeyType == KERB_ETYPE_DES_CBC_CRC)
-    {
-        // all done!
-        goto cleanup;
-    }
+static NTSTATUS
+ConstructTicketRequest(UNICODE_STRING DomainName, PKERB_RETRIEVE_TKT_REQUEST * outRequest,
+                       ULONG * outSize)
+{
+    NTSTATUS Status;
+    UNICODE_STRING TargetPrefix;
+    USHORT TargetSize;
+    ULONG RequestSize;
+    PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
+
+    *outRequest = NULL;
+    *outSize = 0;
 
     //
     // Set up the "krbtgt/" target prefix into a UNICODE_STRING so we
@@ -411,16 +477,15 @@ GetMSTGT(
     TargetPrefix.MaximumLength = TargetPrefix.Length;
 
     //
-    // We will need to concatenate the "krbtgt/" prefix and the previous
-    // response's target domain into our request's target name.
+    // We will need to concatenate the "krbtgt/" prefix and the 
+    // Logon Session's DnsDomainName into our request's target name.
     //
     // Therefore, first compute the necessary buffer size for that.
     //
     // Note that we might theoretically have integer overflow.
     //
 
-    TargetSize = TargetPrefix.Length +
-        pTicketResponse->Ticket.TargetDomainName.Length;
+    TargetSize = TargetPrefix.Length + DomainName.Length;
 
     //
     // The ticket request buffer needs to be a single buffer.  That buffer
@@ -433,13 +498,9 @@ GetMSTGT(
     // Allocate the request buffer and make sure it's zero-filled.
     //
 
-    pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST)
-        LocalAlloc(LMEM_ZEROINIT, RequestSize);
+    pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize);
     if (!pTicketRequest)
-    {
-        Status = GetLastError();
-        goto cleanup;
-    }
+        return GetLastError();
 
     //
     // Concatenate the target prefix with the previous reponse's
@@ -450,28 +511,278 @@ GetMSTGT(
     pTicketRequest->TargetName.MaximumLength = TargetSize;
     pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
     Status = ConcatenateUnicodeStrings(&(pTicketRequest->TargetName),
-                                       TargetPrefix,
-                                       pTicketResponse->Ticket.TargetDomainName);
+                                        TargetPrefix,
+                                        DomainName);
     assert(SUCCEEDED(Status));
+    *outRequest = pTicketRequest;
+    *outSize    = RequestSize;
+    return Status;
+}
 
+//
+// #define ENABLE_PURGING
+// to allow the purging of expired tickets from LSA cache.  This is necessary
+// to force the retrieval of new TGTs.  Microsoft does not appear to retrieve
+// new tickets when they expire.  Instead they continue to accept the expired
+// tickets.  I do not want to enable purging of the LSA cache without testing
+// the side effects in a Windows domain with a machine which has been suspended,
+// removed from the network, and resumed after ticket expiration.
+//
+static BOOL
+GetMSTGT(
+    HANDLE LogonHandle,
+    ULONG PackageId,
+    KERB_EXTERNAL_TICKET **ticket
+    )
+{
     //
-    // Intialize the requst of the request.
+    // INVARIANTS:
+    //
+    //   (FAILED(Status) || FAILED(SubStatus)) ==> error
+    //   bIsLsaError ==> LsaCallAuthenticationPackage() error
+    //
+
+    BOOL bIsLsaError = FALSE;
+    NTSTATUS Status = 0;
+    NTSTATUS SubStatus = 0;
+
+    KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
+    PKERB_RETRIEVE_TKT_REQUEST pTicketRequest;
+    PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
+    ULONG RequestSize;
+    ULONG ResponseSize;
+#ifdef ENABLE_PURGING
+    KERB_PURGE_TKT_CACHE_REQUEST PurgeRequest;
+    int    purge_cache = 0;
+#endif /* ENABLE_PURGING */
+    int    ignore_cache = 0;
+
+    CacheRequest.MessageType = KerbRetrieveTicketMessage;
+    CacheRequest.LogonId.LowPart = 0;
+    CacheRequest.LogonId.HighPart = 0;
+
+    Status = LsaCallAuthenticationPackage(
+        LogonHandle,
+        PackageId,
+        &CacheRequest,
+        sizeof(CacheRequest),
+        &pTicketResponse,
+        &ResponseSize,
+        &SubStatus
+        );
+
+    if (FAILED(Status))
+    {
+        // if the call to LsaCallAuthenticationPackage failed we cannot
+        // perform any queries most likely because the Kerberos package 
+        // is not available or we do not have access
+        bIsLsaError = TRUE;
+        goto cleanup;
+    }
+
+    if (FAILED(SubStatus)) {
+        PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
+        BOOL    Success = FALSE;
+        OSVERSIONINFOEX verinfo;
+        int supported = 0;
+
+        // SubStatus 0x8009030E is not documented.  However, it appears
+        // to mean there is no TGT
+        if (SubStatus != 0x8009030E) {
+            bIsLsaError = TRUE;
+            goto cleanup;
+        }
+
+        verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+        GetVersionEx((OSVERSIONINFO *)&verinfo);
+        supported = (verinfo.dwMajorVersion > 5) || 
+            (verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion >= 1);
+
+        // If we could not get a TGT from the cache we won't know what the
+        // Kerberos Domain should have been.  On Windows XP and 2003 Server
+        // we can extract it from the Security Logon Session Data.  However,
+        // the required fields are not supported on Windows 2000.  :(
+        if ( supported && GetSecurityLogonSessionData(&pSessionData) ) {
+            if ( pSessionData->DnsDomainName.Buffer ) {
+                Status = ConstructTicketRequest(pSessionData->DnsDomainName,
+                                                &pTicketRequest, &RequestSize);
+                if ( FAILED(Status) ) {
+                    goto cleanup;
+                }
+            } else {
+                bIsLsaError = TRUE;
+                goto cleanup;
+            }
+            LsaFreeReturnBuffer(pSessionData);
+        } else {
+            CHAR  UserDnsDomain[256];
+            WCHAR UnicodeUserDnsDomain[256];
+            UNICODE_STRING wrapper;
+            if ( !get_STRING_from_registry(HKEY_CURRENT_USER,
+                                          "Volatile Environment",
+                                          "USERDNSDOMAIN",
+                                           UserDnsDomain,
+                                           sizeof(UserDnsDomain)
+                                           ) )
+            {
+                goto cleanup;
+            }
+
+            ANSIToUnicode(UserDnsDomain,UnicodeUserDnsDomain,256);
+            wrapper.Buffer = UnicodeUserDnsDomain;
+            wrapper.Length = wcslen(UnicodeUserDnsDomain) * sizeof(WCHAR);
+            wrapper.MaximumLength = 256;
+
+            Status = ConstructTicketRequest(wrapper,
+                                             &pTicketRequest, &RequestSize);
+            if ( FAILED(Status) ) {
+                goto cleanup;
+            }
+        }
+    } else {
+#ifdef PURGE_ALL
+        purge_cache = 1;
+#else
+        switch (pTicketResponse->Ticket.SessionKey.KeyType) {
+        case KERB_ETYPE_DES_CBC_CRC:
+        case KERB_ETYPE_DES_CBC_MD4:
+        case KERB_ETYPE_DES_CBC_MD5:
+        case KERB_ETYPE_NULL:
+        case KERB_ETYPE_RC4_HMAC_NT: {
+            FILETIME Now, EndTime, LocalEndTime;
+            GetSystemTimeAsFileTime(&Now);
+            EndTime.dwLowDateTime=pTicketResponse->Ticket.EndTime.LowPart;
+            EndTime.dwHighDateTime=pTicketResponse->Ticket.EndTime.HighPart;
+            FileTimeToLocalFileTime(&EndTime, &LocalEndTime);
+            if (CompareFileTime(&Now, &LocalEndTime) >= 0) {
+#ifdef ENABLE_PURGING
+                purge_cache = 1;
+#else
+                ignore_cache = 1;
+#endif /* ENABLE_PURGING */
+                break;
+            }
+            if (pTicketResponse->Ticket.TicketFlags & KERB_TICKET_FLAGS_invalid) {
+                ignore_cache = 1;
+                break;      // invalid, need to attempt a TGT request
+            }
+            goto cleanup;   // all done
+        }
+        case KERB_ETYPE_RC4_MD4:
+        default:
+            // not supported
+            ignore_cache = 1;
+            break;
+        }
+#endif /* PURGE_ALL */
+
+        Status = ConstructTicketRequest(pTicketResponse->Ticket.TargetDomainName,
+                                        &pTicketRequest, &RequestSize);
+        if ( FAILED(Status) ) {
+            goto cleanup;
+        }
+
+        //
+        // Free the previous response buffer so we can get the new response.
+        //
+
+        if ( pTicketResponse ) {
+            memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE));
+            LsaFreeReturnBuffer(pTicketResponse);
+            pTicketResponse = NULL;
+        }
+
+#ifdef ENABLE_PURGING
+        if ( purge_cache ) {
+            //
+            // Purge the existing tickets which we cannot use so new ones can 
+            // be requested.  It is not possible to purge just the TGT.  All
+            // service tickets must be purged.
+            //
+            PurgeRequest.MessageType = KerbPurgeTicketCacheMessage;
+            PurgeRequest.LogonId.LowPart = 0;
+            PurgeRequest.LogonId.HighPart = 0;
+            PurgeRequest.ServerName.Buffer = L"";
+            PurgeRequest.ServerName.Length = 0;
+            PurgeRequest.ServerName.MaximumLength = 0;
+            PurgeRequest.RealmName.Buffer = L"";
+            PurgeRequest.RealmName.Length = 0;
+            PurgeRequest.RealmName.MaximumLength = 0;
+            Status = LsaCallAuthenticationPackage(LogonHandle,
+                                                    PackageId,
+                                                    &PurgeRequest,
+                                                    sizeof(PurgeRequest),
+                                                    NULL,
+                                                    NULL,
+                                                    &SubStatus
+                                                    );
+        }
+#endif /* ENABLE_PURGING */
+    }
+    
+    //
+    // Intialize the request of the request.
     //
 
     pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
     pTicketRequest->LogonId.LowPart = 0;
     pTicketRequest->LogonId.HighPart = 0;
     // Note: pTicketRequest->TargetName set up above
-    pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_DONT_USE_CACHE;
+#ifdef ENABLE_PURGING
+    pTicketRequest->CacheOptions = ((ignore_cache || !purge_cache) ? 
+                                     KERB_RETRIEVE_TICKET_DONT_USE_CACHE : 0L);
+#else
+    pTicketRequest->CacheOptions = (ignore_cache ? KERB_RETRIEVE_TICKET_DONT_USE_CACHE : 0L);
+#endif /* ENABLE_PURGING */
     pTicketRequest->TicketFlags = 0L;
-    pTicketRequest->EncryptionType = ENCTYPE_DES_CBC_CRC;
+    pTicketRequest->EncryptionType = 0L;
+
+    Status = LsaCallAuthenticationPackage(
+        LogonHandle,
+        PackageId,
+        pTicketRequest,
+        RequestSize,
+        &pTicketResponse,
+        &ResponseSize,
+        &SubStatus
+        );
+
+    if (FAILED(Status) || FAILED(SubStatus))
+    {
+        bIsLsaError = TRUE;
+        goto cleanup;
+    }
+
+    //
+    // Check to make sure the new tickets we received are of a type we support
+    //
+
+    switch (pTicketResponse->Ticket.SessionKey.KeyType) {
+    case KERB_ETYPE_DES_CBC_CRC:
+    case KERB_ETYPE_DES_CBC_MD4:
+    case KERB_ETYPE_DES_CBC_MD5:
+    case KERB_ETYPE_NULL:
+    case KERB_ETYPE_RC4_HMAC_NT:
+        goto cleanup;   // all done
+    case KERB_ETYPE_RC4_MD4:
+    default:
+        // not supported
+        break;
+    }
+
 
     //
-    // Free the previous response buffer so we can get the new response.
+    // Try once more but this time specify the Encryption Type
+    // (This will not store the retrieved tickets in the LSA cache)
     //
+    pTicketRequest->EncryptionType = ENCTYPE_DES_CBC_CRC;
+    pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_DONT_USE_CACHE;
 
-    LsaFreeReturnBuffer(pTicketResponse);
-    pTicketResponse = NULL;
+    if ( pTicketResponse ) {
+        memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE));
+        LsaFreeReturnBuffer(pTicketResponse);
+        pTicketResponse = NULL;
+    }
 
     Status = LsaCallAuthenticationPackage(
         LogonHandle,
@@ -489,7 +800,10 @@ GetMSTGT(
         goto cleanup;
     }
 
- cleanup:
+  cleanup:
+    if ( pTicketRequest )
+        LsaFreeReturnBuffer(pTicketRequest);
+
     if (FAILED(Status) || FAILED(SubStatus))
     {
         if (bIsLsaError)
@@ -505,9 +819,11 @@ GetMSTGT(
             ShowWinError("GetMSTGT", Status);
         }
 
-        if (pTicketResponse)
+        if (pTicketResponse) {
+            memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE));
             LsaFreeReturnBuffer(pTicketResponse);
-
+            pTicketResponse = NULL;
+        }
         return(FALSE);
     }