diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/ChangeLog | 5 | ||||
| -rw-r--r-- | src/lib/Makefile.in | 2 | ||||
| -rw-r--r-- | src/lib/krb5/ccache/ChangeLog | 24 | ||||
| -rw-r--r-- | src/lib/krb5/ccache/Makefile.in | 6 | ||||
| -rw-r--r-- | src/lib/krb5/ccache/cc_mslsa.c | 1265 | ||||
| -rw-r--r-- | src/lib/krb5/ccache/ccbase.c | 8 | ||||
| -rw-r--r-- | src/lib/krb5/error_tables/ChangeLog | 4 | ||||
| -rw-r--r-- | src/lib/krb5/error_tables/krb5_err.et | 2 | ||||
| -rw-r--r-- | src/windows/ms2mit/ChangeLog | 11 | ||||
| -rw-r--r-- | src/windows/ms2mit/Makefile.in | 2 | ||||
| -rw-r--r-- | src/windows/ms2mit/ms2mit.c | 852 |
11 files changed, 1349 insertions, 832 deletions
diff --git a/src/lib/ChangeLog b/src/lib/ChangeLog index 6e3b19467..c6777db66 100644 --- a/src/lib/ChangeLog +++ b/src/lib/ChangeLog @@ -1,3 +1,8 @@ +2003-12-11 Jeffrey Altman <jaltman@mit.edu> + + * Makefile.in: Add secur32.lib to libraries necessary to build + krb5_32.dll. Necessary to support the new MSLSA ccache type. + 2003-12-08 Jeffrey Altman <jaltman@mit.edu> * krb4_32.def: Add exports for functions exported by KfM diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in index 01a43511d..030beb7bd 100644 --- a/src/lib/Makefile.in +++ b/src/lib/Makefile.in @@ -52,7 +52,7 @@ KRB5RC = krb5.rc VERSIONRC = $(BUILDTOP)\windows\version.rc WINLIBS = kernel32.lib ws2_32.lib user32.lib shell32.lib oldnames.lib \ - version.lib advapi32.lib gdi32.lib + version.lib secur32.lib advapi32.lib gdi32.lib WINDLLFLAGS = $(DLL_LINKOPTS) -base:0x1c000000 NO_GLUE=$(OUTPRE)no_glue.obj diff --git a/src/lib/krb5/ccache/ChangeLog b/src/lib/krb5/ccache/ChangeLog index 9bfb82fac..d8a5ec94e 100644 --- a/src/lib/krb5/ccache/ChangeLog +++ b/src/lib/krb5/ccache/ChangeLog @@ -1,3 +1,27 @@ +2003-12-11 Jeffrey Altman <jaltman@mit.edu> + + * Makefile.in, ccbase.c, cc_mslsa.c (new) + + Remove all of the code which was duplicated between ms2mit.c + and the KfW Leash libraries (and who knows how many applications + shipped by third parties) and use it as the basis for a new + krb5_ccache type, "MSLSA:". The "MSLSA:" ccache type is a + read-only ccache which can be used either as a monitor of the + contents of the Microsoft LSA cache or as a source for copying + the contents to another ccache type. The purpose of migrating + this code to the krb5_32.dll is to avoid the need for applications + to be consistently updated each time Microsoft makes a change + to the behavior of the LSA cache. Changes have occurred with + the release of 2000, XP, and 2003 so far. Also, the code for + working with the MS LSA cache is not well documented and many + mistakes were made in the original versions of the ms2mit.c + code base. Unfortunately, the ms2mit.c code has been copied + into many other applications. + + With access to this new ccache type, the ms2mit.c source file + is reduced from 890 lines to 80 lines including the copyright + banner. + 2003-11-26 Jeffrey Altman <jaltman@mit.edu> * cc_default.c: Add support for Leash Kinit Dialog on Windows to diff --git a/src/lib/krb5/ccache/Makefile.in b/src/lib/krb5/ccache/Makefile.in index 937c586f3..6b3e4a441 100644 --- a/src/lib/krb5/ccache/Makefile.in +++ b/src/lib/krb5/ccache/Makefile.in @@ -25,6 +25,7 @@ STLIBOBJS= \ ccdefops.o \ cc_retr.o \ cc_file.o cc_memory.o \ +##WIN32## cc_mslsa.c \ ccfns.o \ ser_cc.o @@ -35,6 +36,7 @@ OBJS= $(OUTPRE)ccbase.$(OBJEXT) \ $(OUTPRE)cc_retr.$(OBJEXT) \ $(OUTPRE)cc_file.$(OBJEXT) \ $(OUTPRE)cc_memory.$(OBJEXT) \ +##WIN32## $(OUTPRE)cc_mslsa.$(OBJEXT) \ $(OUTPRE)ccfns.$(OBJEXT) \ $(OUTPRE)ser_cc.$(OBJEXT) @@ -45,6 +47,7 @@ SRCS= $(srcdir)/ccbase.c \ $(srcdir)/cc_retr.c \ $(srcdir)/cc_file.c \ $(srcdir)/cc_memory.c \ +##WIN32## $(srcdir)cc_mslsa.c \ $(srcdir)/ccfns.c \ $(srcdir)/ser_cc.c @@ -139,6 +142,9 @@ cc_memory.so cc_memory.po $(OUTPRE)cc_memory.$(OBJEXT): cc_memory.c $(SRCTOP)/in $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ $(SRCTOP)/include/krb5/kdb.h +##WIN32##cc_mslsa.so cc_mslsa.po $(OUTPRE)cc_mslsa.$(OBJEXT): cc_mslsa.c $(SRCTOP)/include/k5-int.h \ +##WIN32## $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h \ +##WIN32## $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) ccfns.so ccfns.po $(OUTPRE)ccfns.$(OBJEXT): ccfns.c $(SRCTOP)/include/k5-int.h \ $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h \ $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \ diff --git a/src/lib/krb5/ccache/cc_mslsa.c b/src/lib/krb5/ccache/cc_mslsa.c new file mode 100644 index 000000000..72a2b7fbf --- /dev/null +++ b/src/lib/krb5/ccache/cc_mslsa.c @@ -0,0 +1,1265 @@ +/* + * lib/krb5/ccache/cc_mslsa.c + * + * Copyright 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. + * + * Copyright 2000 by Carnegie Mellon University + * + * All Rights Reserved + * + * 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 Carnegie Mellon + * University not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR + * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * 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. + * + * Implementation of read-only microsoft windows lsa credentials cache + */ + +#ifdef _WIN32 +#define UNICODE +#define _UNICODE + +#include "k5-int.h" +#include "com_err.h" + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <conio.h> +#include <time.h> +#define SECURITY_WIN32 +#include <security.h> +#include <ntsecapi.h> + +static VOID +ShowWinError(LPSTR szAPI, DWORD dwError) +{ +#define MAX_MSG_SIZE 256 + + // TODO - Write errors to event log so that scripts that don't + // check for errors will still get something in the event log + + WCHAR szMsgBuf[MAX_MSG_SIZE]; + DWORD dwRes; + + printf("Error calling function %s: %lu\n", szAPI, dwError); + + dwRes = FormatMessage ( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwError, + MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), + szMsgBuf, + MAX_MSG_SIZE, + NULL); + if (0 == dwRes) { + printf("FormatMessage failed with %d\n", GetLastError()); + ExitProcess(EXIT_FAILURE); + } + + printf("%S",szMsgBuf); +} + +static VOID +ShowLsaError(LPSTR szAPI, NTSTATUS Status) +{ + // + // Convert the NTSTATUS to Winerror. Then call ShowWinError(). + // + ShowWinError(szAPI, LsaNtStatusToWinError(Status)); +} + + + +static BOOL +WINAPI +UnicodeToANSI(LPTSTR lpInputString, LPSTR lpszOutputString, int nOutStringLen) +{ + CPINFO CodePageInfo; + + GetCPInfo(CP_ACP, &CodePageInfo); + + if (CodePageInfo.MaxCharSize > 1) + // Only supporting non-Unicode strings + return FALSE; + else if (((LPBYTE) lpInputString)[1] == '\0') + { + // Looks like unicode, better translate it + WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) lpInputString, -1, + lpszOutputString, nOutStringLen, NULL, NULL); + } + else + lstrcpyA(lpszOutputString, (LPSTR) lpInputString); + return TRUE; +} // UnicodeToANSI + +static VOID +WINAPI +ANSIToUnicode(LPSTR lpInputString, LPTSTR lpszOutputString, int nOutStringLen) +{ + + CPINFO CodePageInfo; + + lstrcpy(lpszOutputString, (LPTSTR) lpInputString); + + GetCPInfo(CP_ACP, &CodePageInfo); + + if (CodePageInfo.MaxCharSize > 1) + // It must already be a Unicode string + return; + else if (((LPBYTE) lpInputString)[1] != '\0') + { + // Looks like ANSI, better translate it + MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lpInputString, -1, + (LPWSTR) lpszOutputString, nOutStringLen); + } + else + lstrcpy(lpszOutputString, (LPTSTR) lpInputString); +} // ANSIToUnicode + + +static void +MSPrincToMITPrinc(KERB_EXTERNAL_NAME *msprinc, WCHAR *realm, krb5_context context, krb5_principal *principal) +{ + WCHAR princbuf[512],tmpbuf[128]; + char aname[512]; + USHORT i; + princbuf[0]=0; + for (i=0;i<msprinc->NameCount;i++) { + wcsncpy(tmpbuf, msprinc->Names[i].Buffer, + msprinc->Names[i].Length/sizeof(WCHAR)); + tmpbuf[msprinc->Names[i].Length/sizeof(WCHAR)]=0; + if (princbuf[0]) + wcscat(princbuf, L"/"); + wcscat(princbuf, tmpbuf); + } + wcscat(princbuf, L"@"); + wcscat(princbuf, realm); + UnicodeToANSI(princbuf, aname, sizeof(aname)); + krb5_parse_name(context, aname, principal); +} + + +static time_t +FileTimeToUnixTime(LARGE_INTEGER *ltime) +{ + FILETIME filetime, localfiletime; + SYSTEMTIME systime; + struct tm utime; + filetime.dwLowDateTime=ltime->LowPart; + filetime.dwHighDateTime=ltime->HighPart; + FileTimeToLocalFileTime(&filetime, &localfiletime); + FileTimeToSystemTime(&localfiletime, &systime); + utime.tm_sec=systime.wSecond; + utime.tm_min=systime.wMinute; + utime.tm_hour=systime.wHour; + utime.tm_mday=systime.wDay; + utime.tm_mon=systime.wMonth-1; + utime.tm_year=systime.wYear-1900; + utime.tm_isdst=-1; + return(mktime(&utime)); +} + +static void +MSSessionKeyToMITKeyblock(KERB_CRYPTO_KEY *mskey, krb5_context context, krb5_keyblock *keyblock) +{ + krb5_keyblock tmpblock; + tmpblock.magic=KV5M_KEYBLOCK; + tmpblock.enctype=mskey->KeyType; + tmpblock.length=mskey->Length; + tmpblock.contents=mskey->Value; + krb5_copy_keyblock_contents(context, &tmpblock, keyblock); +} + + +static void +MSFlagsToMITFlags(ULONG msflags, ULONG *mitflags) +{ + *mitflags=msflags; +} + +static void +MSTicketToMITTicket(KERB_EXTERNAL_TICKET *msticket, krb5_context context, krb5_data *ticket) +{ + krb5_data tmpdata, *newdata; + tmpdata.magic=KV5M_DATA; + tmpdata.length=msticket->EncodedTicketSize; + tmpdata.data=msticket->EncodedTicket; + // todo: fix this up a little. this is ugly and will break krb_free_data() + krb5_copy_data(context, &tmpdata, &newdata); + memcpy(ticket, newdata, sizeof(krb5_data)); +} + +static void +MSCredToMITCred(KERB_EXTERNAL_TICKET *msticket, krb5_context context, krb5_creds *creds) +{ + WCHAR wtmp[128]; + ZeroMemory(creds, sizeof(krb5_creds)); + creds->magic=KV5M_CREDS; + wcsncpy(wtmp, msticket->TargetDomainName.Buffer, + msticket->TargetDomainName.Length/sizeof(WCHAR)); + wtmp[msticket->TargetDomainName.Length/sizeof(WCHAR)]=0; + MSPrincToMITPrinc(msticket->ClientName, wtmp, context, &creds->client); + wcsncpy(wtmp, msticket->DomainName.Buffer, + msticket->DomainName.Length/sizeof(WCHAR)); + wtmp[msticket->DomainName.Length/sizeof(WCHAR)]=0; + MSPrincToMITPrinc(msticket->ServiceName, wtmp, context, &creds->server); + MSSessionKeyToMITKeyblock(&msticket->SessionKey, context, + &creds->keyblock); + MSFlagsToMITFlags(msticket->TicketFlags, &creds->ticket_flags); + creds->times.starttime=FileTimeToUnixTime(&msticket->StartTime); + creds->times.endtime=FileTimeToUnixTime(&msticket->EndTime); + creds->times.renew_till=FileTimeToUnixTime(&msticket->RenewUntil); + + /* MS Tickets are addressless. MIT requires an empty address + * not a NULL list of addresses. + */ + creds->addresses = (krb5_address **)malloc(sizeof(krb5_address *)); + memset(creds->addresses, 0, sizeof(krb5_address *)); + + MSTicketToMITTicket(msticket, context, &creds->ticket); +} + +static BOOL +PackageConnectLookup(HANDLE *pLogonHandle, ULONG *pPackageId) +{ + LSA_STRING Name; + NTSTATUS Status; + + Status = LsaConnectUntrusted( + pLogonHandle + ); + + if (FAILED(Status)) + { + ShowLsaError("LsaConnectUntrusted", Status); + return FALSE; + } + + Name.Buffer = MICROSOFT_KERBEROS_NAME_A; + Name.Length = strlen(Name.Buffer); + Name.MaximumLength = Name.Length + 1; + + Status = LsaLookupAuthenticationPackage( + *pLogonHandle, + &Name, + pPackageId + ); + + if (FAILED(Status)) + { + ShowLsaError("LsaLookupAuthenticationPackage", Status); + return FALSE; + } + + return TRUE; + +} + + +static DWORD +ConcatenateUnicodeStrings(UNICODE_STRING *pTarget, UNICODE_STRING Source1, UNICODE_STRING Source2) +{ + // + // The buffers for Source1 and Source2 cannot overlap pTarget's + // buffer. Source1.Length + Source2.Length must be <= 0xFFFF, + // otherwise we overflow... + // + + USHORT TotalSize = Source1.Length + Source2.Length; + PBYTE buffer = (PBYTE) pTarget->Buffer; + + if (TotalSize > pTarget->MaximumLength) + return ERROR_INSUFFICIENT_BUFFER; + + pTarget->Length = TotalSize; + memcpy(buffer, Source1.Buffer, Source1.Length); + memcpy(buffer + Source1.Length, Source2.Buffer, Source2.Length); + return ERROR_SUCCESS; +} + +static BOOL +get_STRING_from_registry(HKEY hBaseKey, char * key, char * value, char * outbuf, DWORD outlen) +{ + HKEY hKey; + DWORD dwCount; + LONG rc; + + if (!outbuf || outlen == 0) + return 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; + HANDLE TokenHandle; + TOKEN_STATISTICS Stats; + DWORD ReqLen; + BOOL Success; + + if (!ppSessionData) + return FALSE; + *ppSessionData = NULL; + + Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle ); + if ( !Success ) + return FALSE; + + Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen ); + CloseHandle( TokenHandle ); + if ( !Success ) + return FALSE; + + Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData ); + if ( FAILED(Status) || !ppSessionData ) + return FALSE; + + return TRUE; +} + +// +// 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; +} + +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 + // can easily concatenate it later. + // + + TargetPrefix.Buffer = L"krbtgt/"; + TargetPrefix.Length = wcslen(TargetPrefix.Buffer) * sizeof(WCHAR); + TargetPrefix.MaximumLength = TargetPrefix.Length; + + // + // 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 + DomainName.Length; + + // + // The ticket request buffer needs to be a single buffer. That buffer + // needs to include the buffer for the target name. + // + + RequestSize = sizeof(*pTicketRequest) + TargetSize; + + // + // Allocate the request buffer and make sure it's zero-filled. + // + + pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize); + if (!pTicketRequest) + return GetLastError(); + + // + // Concatenate the target prefix with the previous reponse's + // target domain. + // + + pTicketRequest->TargetName.Length = 0; + pTicketRequest->TargetName.MaximumLength = TargetSize; + pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1); + Status = ConcatenateUnicodeStrings(&(pTicketRequest->TargetName), + TargetPrefix, + DomainName); + *outRequest = pTicketRequest; + *outSize = RequestSize; + return Status; +} + +static BOOL +PurgeMSTGT(HANDLE LogonHandle, ULONG PackageId) +{ + NTSTATUS Status = 0; + NTSTATUS SubStatus = 0; + KERB_PURGE_TKT_CACHE_REQUEST PurgeRequest; + + 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 + ); + if (FAILED(Status) || FAILED(SubStatus)) + return FALSE; + return TRUE; +} + +// +// #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) +{ + // + // 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 + 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, MinLife, EndTime, LocalEndTime; + __int64 temp; + // FILETIME is in units of 100 nano-seconds + // If obtained tickets are either expired or have a lifetime + // less than 20 minutes, retry ... + GetSystemTimeAsFileTime(&Now); + EndTime.dwLowDateTime=pTicketResponse->Ticket.EndTime.LowPart; + EndTime.dwHighDateTime=pTicketResponse->Ticket.EndTime.HighPart; + FileTimeToLocalFileTime(&EndTime, &LocalEndTime); + temp = Now.dwHighDateTime; + temp <<= 32; + temp = Now.dwLowDateTime; + temp += 1200 * 10000; + MinLife.dwHighDateTime = (DWORD)((temp >> 32) & 0xFFFFFFFF); + MinLife.dwLowDateTime = (DWORD)(temp & 0xFFFFFFFF); + if (CompareFileTime(&MinLife, &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. + // + PurgeMSTGT(LogonHandle, PackageId); + } +#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 +#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 = 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; + } + + + // + // 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; + + if ( pTicketResponse ) { + memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE)); + LsaFreeReturnBuffer(pTicketResponse); + pTicketResponse = NULL; + } + + Status = LsaCallAuthenticationPackage( + LogonHandle, + PackageId, + pTicketRequest, + RequestSize, + &pTicketResponse, + &ResponseSize, + &SubStatus + ); + + if (FAILED(Status) || FAILED(SubStatus)) + { + bIsLsaError = TRUE; + goto cleanup; + } + + cleanup: + if ( pTicketRequest ) + LsaFreeReturnBuffer(pTicketRequest); + + if (FAILED(Status) || FAILED(SubStatus)) + { + if (bIsLsaError) + { + // XXX - Will be fixed later + if (FAILED(Status)) + ShowLsaError("LsaCallAuthenticationPackage", Status); + if (FAILED(SubStatus)) + ShowLsaError("LsaCallAuthenticationPackage", SubStatus); + } + else + { + ShowWinError("GetMSTGT", Status); + } + + if (pTicketResponse) { + memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE)); + LsaFreeReturnBuffer(pTicketResponse); + pTicketResponse = NULL; + } + return(FALSE); + } + + *ticket = &(pTicketResponse->Ticket); + return(TRUE); +} + +static BOOL +GetQueryTktCacheResponse( HANDLE LogonHandle, ULONG PackageId, + PKERB_QUERY_TKT_CACHE_RESPONSE * ppResponse) +{ + NTSTATUS Status = 0; + NTSTATUS SubStatus = 0; + + KERB_QUERY_TKT_CACHE_REQUEST CacheRequest; + PKERB_QUERY_TKT_CACHE_RESPONSE pQueryResponse = NULL; + ULONG ResponseSize; + + CacheRequest.MessageType = KerbQueryTicketCacheMessage; + CacheRequest.LogonId.LowPart = 0; + CacheRequest.LogonId.HighPart = 0; + + Status = LsaCallAuthenticationPackage( + LogonHandle, + PackageId, + &CacheRequest, + sizeof(CacheRequest), + &pQueryResponse, + &ResponseSize, + &SubStatus + ); + + if ( !(FAILED(Status) || FAILED(SubStatus)) ) { + *ppResponse = pQueryResponse; + return TRUE; + } + + return FALSE; +} + +static void +FreeQueryResponse(PKERB_QUERY_TKT_CACHE_RESPONSE pResponse) +{ + LsaFreeReturnBuffer(pResponse); +} + +static BOOL +GetMSCacheTicket( HANDLE LogonHandle, ULONG PackageId, + PKERB_TICKET_CACHE_INFO tktinfo, PKERB_EXTERNAL_TICKET *ticket) +{ + NTSTATUS Status = 0; + NTSTATUS SubStatus = 0; + ULONG RequestSize; + PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL; + PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL; + ULONG ResponseSize; + + RequestSize = sizeof(*pTicketRequest) + tktinfo->ServerName.Length; + + pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize); + if (!pTicketRequest) + return FALSE; + + pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage; + pTicketRequest->LogonId.LowPart = 0; + pTicketRequest->LogonId.HighPart = 0; + pTicketRequest->TargetName.Length = tktinfo->ServerName.Length; + pTicketRequest->TargetName.MaximumLength = tktinfo->ServerName.Length; + pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1); + memcpy(pTicketRequest->TargetName.Buffer,tktinfo->ServerName.Buffer, tktinfo->ServerName.Length); + pTicketRequest->CacheOptions = 0; + pTicketRequest->EncryptionType = tktinfo->EncryptionType; + pTicketRequest->TicketFlags = tktinfo->TicketFlags; + + Status = LsaCallAuthenticationPackage( + LogonHandle, + PackageId, + pTicketRequest, + RequestSize, + &pTicketResponse, + &ResponseSize, + &SubStatus + ); + + LsaFreeReturnBuffer(pTicketRequest); + + if (FAILED(Status) || FAILED(SubStatus)) + return(FALSE); + + /* otherwise return ticket */ + *ticket = &(pTicketResponse->Ticket); + return(TRUE); + +} + +static krb5_error_code KRB5_CALLCONV krb5_lcc_close + (krb5_context, krb5_ccache id); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_destroy + (krb5_context, krb5_ccache id); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_end_seq_get + (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_generate_new + (krb5_context, krb5_ccache *id); + +static const char * KRB5_CALLCONV krb5_lcc_get_name + (krb5_context, krb5_ccache id); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_get_principal + (krb5_context, krb5_ccache id, krb5_principal *princ); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_initialize + (krb5_context, krb5_ccache id, krb5_principal princ); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_next_cred + (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor, + krb5_creds *creds); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_resolve + (krb5_context, krb5_ccache *id, const char *residual); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_retrieve + (krb5_context, krb5_ccache id, krb5_flags whichfields, + krb5_creds *mcreds, krb5_creds *creds); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_start_seq_get + (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_store + (krb5_context, krb5_ccache id, krb5_creds *creds); + +static krb5_error_code KRB5_CALLCONV krb5_lcc_set_flags + (krb5_context, krb5_ccache id, krb5_flags flags); + +extern const krb5_cc_ops krb5_lcc_ops; + +krb5_error_code krb5_change_cache (void); + +#define KRB5_OK 0 + +typedef struct _krb5_lcc_data { + HANDLE LogonHandle; + ULONG PackageId; + char * cc_name; + krb5_principal princ; +} krb5_lcc_data; + +typedef struct _krb5_lcc_cursor { + PKERB_QUERY_TKT_CACHE_RESPONSE response; + int index; +} krb5_lcc_cursor; + + +/* + * Requires: + * residual is ignored + * + * Modifies: + * id + * + * Effects: + * creates a file-based cred cache that will reside in the file + * residual. The cache is not opened, but the filename is reserved. + * + * Returns: + * A filled in krb5_ccache structure "id". + * + * Errors: + * KRB5_CC_NOMEM - there was insufficient memory to allocate the + * + * krb5_ccache. id is undefined. + * permission errors + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_resolve (krb5_context context, krb5_ccache *id, const char *residual) +{ + krb5_ccache lid; + krb5_lcc_data *data; + HANDLE LogonHandle; + ULONG PackageId; + KERB_EXTERNAL_TICKET *msticket; + + if (!IsKerberosLogon()) + return KRB5_FCC_NOFILE; + + if(!PackageConnectLookup(&LogonHandle, &PackageId)) + return KRB5_FCC_NOFILE; + + lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); + if (lid == NULL) { + CloseHandle(LogonHandle); + return KRB5_CC_NOMEM; + } + + lid->ops = &krb5_lcc_ops; + + lid->data = (krb5_pointer) malloc(sizeof(krb5_lcc_data)); + if (lid->data == NULL) { + krb5_xfree(lid); + CloseHandle(LogonHandle); + return KRB5_CC_NOMEM; + } + + lid->magic = KV5M_CCACHE; + data = (krb5_lcc_data *)lid->data; + data->LogonHandle = LogonHandle; + data->PackageId = PackageId; + + data->cc_name = (char *)malloc(strlen(residual)+1); + if (data->cc_name == NULL) { + krb5_xfree(lid->data); + krb5_xfree(lid); + CloseHandle(LogonHandle); + return KRB5_CC_NOMEM; + } + strcpy(data->cc_name, residual); + + /* + * we must obtain a tgt from the cache in order to determine the principal + */ + if (GetMSTGT(data->LogonHandle, data->PackageId, &msticket)) { + /* convert the ticket */ + krb5_creds creds; + MSCredToMITCred(msticket, context, &creds); + LsaFreeReturnBuffer(msticket); + + krb5_copy_principal(context, creds.client, &data->princ); + krb5_free_cred_contents(context,&creds); + } else { + data->princ = 0; + } + + /* + * other routines will get errors on open, and callers must expect them, + * if cache is non-existent/unusable + */ + *id = lid; + return KRB5_OK; +} + +/* + * not supported + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ) +{ + return KRB5_CC_READONLY; +} + + +/* + * Modifies: + * id + * + * Effects: + * Closes the microsoft lsa cache, invalidates the id, and frees any resources + * associated with the cache. + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_close(krb5_context context, krb5_ccache id) +{ + register int closeval = KRB5_OK; + register krb5_lcc_data *data = (krb5_lcc_data *) id->data; + + CloseHandle(data->LogonHandle); + + krb5_xfree(data); + krb5_xfree(id); + + return closeval; +} + +/* + * Effects: + * Destroys the contents of id. + * + * Errors: + * system errors + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_destroy(krb5_context context, krb5_ccache id) +{ + register krb5_lcc_data *data = (krb5_lcc_data *) id->data; + + return PurgeMSTGT(data->LogonHandle, data->PackageId) ? KRB5_FCC_INTERNAL : KRB5_OK; +} + +/* + * Effects: + * Prepares for a sequential search of the credentials cache. + * Returns a krb5_cc_cursor to be used with krb5_lcc_next_cred and + * krb5_lcc_end_seq_get. + * + * If the cache is modified between the time of this call and the time + * of the final krb5_lcc_end_seq_get, the results are undefined. + * + * Errors: + * KRB5_CC_NOMEM + * KRB5_FCC_INTERNAL - system errors + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor) +{ + krb5_lcc_cursor *lcursor; + krb5_lcc_data *data = (krb5_lcc_data *)id->data; + + lcursor = (krb5_lcc_cursor *) malloc(sizeof(krb5_lcc_cursor)); + if (lcursor == NULL) + return KRB5_CC_NOMEM; + + if ( !GetQueryTktCacheResponse(data->LogonHandle, data->PackageId, &lcursor->response) ) { + free(lcursor); + KRB5_FCC_INTERNAL; + } + lcursor->index = 0; + *cursor = (krb5_cc_cursor) lcursor; + + return KRB5_OK; +} + + +/* + * Requires: + * cursor is a krb5_cc_cursor originally obtained from + * krb5_lcc_start_seq_get. + * + * Modifes: + * cursor + * + * Effects: + * Fills in creds with the TGT obtained from the MS LSA + * + * The cursor is updated to indicate TGT retrieval + * + * Errors: + * KRB5_CC_END + * KRB5_FCC_INTERNAL - system errors + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor, krb5_creds *creds) +{ + krb5_lcc_cursor *lcursor = (krb5_lcc_cursor *) *cursor; + krb5_lcc_data *data = (krb5_lcc_data *)id->data; + KERB_EXTERNAL_TICKET *msticket; + + if ( lcursor->index >= lcursor->response->CountOfTickets ) + return KRB5_CC_END; + + if (!GetMSCacheTicket(data->LogonHandle, data->PackageId, + &lcursor->response->Tickets[lcursor->index++],&msticket)) + return KRB5_FCC_INTERNAL; + + /* convert the ticket */ + MSCredToMITCred(msticket, context, creds); + + LsaFreeReturnBuffer(msticket); + return KRB5_OK; +} + +/* + * Requires: + * cursor is a krb5_cc_cursor originally obtained from + * krb5_lcc_start_seq_get. + * + * Modifies: + * id, cursor + * + * Effects: + * Finishes sequential processing of the file credentials ccache id, + * and invalidates the cursor (it must never be used after this call). + */ +/* ARGSUSED */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor) +{ + krb5_lcc_cursor *lcursor = (krb5_lcc_cursor *) *cursor; + + LsaFreeReturnBuffer(lcursor->response); + free(*cursor); + return KRB5_OK; +} + + +/* + * Errors: + * KRB5_CC_READONLY - not supported + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_generate_new (krb5_context context, krb5_ccache *id) +{ + return KRB5_CC_READONLY; +} + +/* + * Requires: + * id is a ms lsa credential cache + * + * Returns: + * The ccname specified during the krb5_lcc_resolve call + */ +static const char * KRB5_CALLCONV +krb5_lcc_get_name (krb5_context context, krb5_ccache id) +{ + return (char *) ((krb5_lcc_data *) id->data)->cc_name; +} + +/* + * Modifies: + * id, princ + * + * Effects: + * Retrieves the primary principal from id, as set with + * krb5_lcc_initialize. The principal is returned is allocated + * storage that must be freed by the caller via krb5_free_principal. + * + * Errors: + * system errors + * KRB5_CC_NOMEM + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ) +{ + krb5_error_code kret = KRB5_OK; + + /* obtain principal */ + return krb5_copy_principal(context, ((krb5_lcc_data *) id->data)->princ, princ); +} + + +static krb5_error_code KRB5_CALLCONV +krb5_lcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, + krb5_creds *mcreds, krb5_creds *creds) +{ + return krb5_cc_retrieve_cred_default (context, id, whichfields, + mcreds, creds); +} + + +/* + * Errors: + * KRB5_CC_READONLY - not supported + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds) +{ + return KRB5_CC_READONLY; +} + + +/* + * Effects: + * None - ignored + */ +static krb5_error_code KRB5_CALLCONV +krb5_lcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags) +{ + return KRB5_OK; +} + +const krb5_cc_ops krb5_lcc_ops = { + 0, + "MSLSA", + krb5_lcc_get_name, + krb5_lcc_resolve, + krb5_lcc_generate_new, + krb5_lcc_initialize, + krb5_lcc_destroy, + krb5_lcc_close, + krb5_lcc_store, + krb5_lcc_retrieve, + krb5_lcc_get_principal, + krb5_lcc_start_seq_get, + krb5_lcc_next_cred, + krb5_lcc_end_seq_get, + NULL, /* krb5_lcc_remove, */ + krb5_lcc_set_flags +}; +#endif /* _WIN32 */
\ No newline at end of file diff --git a/src/lib/krb5/ccache/ccbase.c b/src/lib/krb5/ccache/ccbase.c index cfe96ec05..f17870663 100644 --- a/src/lib/krb5/ccache/ccbase.c +++ b/src/lib/krb5/ccache/ccbase.c @@ -38,14 +38,20 @@ struct krb5_cc_typelist }; extern const krb5_cc_ops krb5_mcc_ops; +#ifdef _WIN32 +extern const krb5_cc_ops krb5_lcc_ops; +static struct krb5_cc_typelist cc_lcc_entry = { &krb5_lcc_ops, NULL }; +static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, &cc_lcc_entry }; +#else static struct krb5_cc_typelist cc_mcc_entry = { &krb5_mcc_ops, NULL }; +#endif static struct krb5_cc_typelist cc_fcc_entry = { &krb5_cc_file_ops, &cc_mcc_entry }; - static struct krb5_cc_typelist *cc_typehead = &cc_fcc_entry; + /* * Register a new credentials cache type * If override is set, replace any existing ccache with that type tag diff --git a/src/lib/krb5/error_tables/ChangeLog b/src/lib/krb5/error_tables/ChangeLog index 2eb6925ac..c5f1371b8 100644 --- a/src/lib/krb5/error_tables/ChangeLog +++ b/src/lib/krb5/error_tables/ChangeLog @@ -1,3 +1,7 @@ +2003-12-12 Jeffrey Altman <jaltman@mit.edu> + + * krb5_err.et (KRB5_CC_READONLY) new ccache error code + 2003-07-19 Ezra Peisach <epeisach@mit.edu> * init_ets.c (krb5_init_ets): Only initialize error tables once - diff --git a/src/lib/krb5/error_tables/krb5_err.et b/src/lib/krb5/error_tables/krb5_err.et index 66bdebbfb..622143b7d 100644 --- a/src/lib/krb5/error_tables/krb5_err.et +++ b/src/lib/krb5/error_tables/krb5_err.et @@ -338,4 +338,6 @@ error_code KRB5_ERR_BAD_S2K_PARAMS, "Invalid key generation parameters from KDC" error_code KRB5_ERR_NO_SERVICE, "service not available" +error_code KRB5_CC_READONLY, "Ccache function not supported: read-only ccache type" + end diff --git a/src/windows/ms2mit/ChangeLog b/src/windows/ms2mit/ChangeLog index f177bb41d..71577d979 100644 --- a/src/windows/ms2mit/ChangeLog +++ b/src/windows/ms2mit/ChangeLog @@ -1,3 +1,14 @@ +2003-12-11 Jeffrey Altman <jaltman@mit.edu> + + * ms2mit.c, Makefile.in: + + Remove all of the code that manipulates the MS LSA cache. Instead + of reading in the TGT directly we now take advantage of the new + "MSLSA:" krb5_ccache type. We open the MS LSA cache as a read-only + ccache and copy it to the default ccache for the system. + + This removes the dependency on secur32.dll from this file. + 2003-10-21 Jeffrey Altman <jaltman@mit.edu> * ms2mit.c: diff --git a/src/windows/ms2mit/Makefile.in b/src/windows/ms2mit/Makefile.in index a7c6c1ed9..45004e14a 100644 --- a/src/windows/ms2mit/Makefile.in +++ b/src/windows/ms2mit/Makefile.in @@ -13,7 +13,7 @@ PROG_LIBPATH=-L$(TOPLIBD) -L$(KRB5_LIBDIR) all-windows:: $(OUTPRE)ms2mit.exe $(OUTPRE)ms2mit.exe: $(OUTPRE)ms2mit.obj - link $(EXE_LINKOPTS) -out:$@ $(OUTPRE)ms2mit.obj user32.lib secur32.lib advapi32.lib $(KLIB) $(CLIB) + link $(EXE_LINKOPTS) -out:$@ $(OUTPRE)ms2mit.obj user32.lib advapi32.lib $(KLIB) $(CLIB) install:: copy $(OUTPRE)ms2mit.exe $(DESTDIR) diff --git a/src/windows/ms2mit/ms2mit.c b/src/windows/ms2mit/ms2mit.c index 12e028e0b..4674a4b71 100644 --- a/src/windows/ms2mit/ms2mit.c +++ b/src/windows/ms2mit/ms2mit.c @@ -2,28 +2,6 @@ * ms2mit.c * */ -/*********************************************************** - Copyright 2000 by Carnegie Mellon University - - All Rights Reserved - -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 Carnegie Mellon -University not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR -ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -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. @@ -49,797 +27,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ -#define UNICODE -#define _UNICODE - -#include <windows.h> -#include <stdio.h> -#include <stdlib.h> -#include <conio.h> -#include <time.h> -#define SECURITY_WIN32 -#include <security.h> -#include <ntsecapi.h> - -#include <krb5.h> -#include <com_err.h> -#include <assert.h> - -VOID -ShowWinError( - LPSTR szAPI, - DWORD dwError - ) -{ -#define MAX_MSG_SIZE 256 - - // TODO - Write errors to event log so that scripts that don't - // check for errors will still get something in the event log - - WCHAR szMsgBuf[MAX_MSG_SIZE]; - DWORD dwRes; - - printf("Error calling function %s: %lu\n", szAPI, dwError); - - dwRes = FormatMessage ( - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - dwError, - MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), - szMsgBuf, - MAX_MSG_SIZE, - NULL); - if (0 == dwRes) { - printf("FormatMessage failed with %d\n", GetLastError()); - ExitProcess(EXIT_FAILURE); - } - - printf("%S",szMsgBuf); -} - -VOID -ShowLsaError( - LPSTR szAPI, - NTSTATUS Status - ) -{ - // - // Convert the NTSTATUS to Winerror. Then call ShowWinError(). - // - ShowWinError(szAPI, LsaNtStatusToWinError(Status)); -} - - - -BOOL -WINAPI -UnicodeToANSI( - LPTSTR lpInputString, - LPSTR lpszOutputString, - int nOutStringLen - ) -{ -#ifndef WIN32S - CPINFO CodePageInfo; - - GetCPInfo(CP_ACP, &CodePageInfo); - - if (CodePageInfo.MaxCharSize > 1) - // Only supporting non-Unicode strings - return FALSE; - else if (((LPBYTE) lpInputString)[1] == '\0') - { - // Looks like unicode, better translate it - WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) lpInputString, -1, - lpszOutputString, nOutStringLen, NULL, NULL); - } - else - lstrcpyA(lpszOutputString, (LPSTR) lpInputString); -#else - lstrcpy(lpszOutputString, (LPSTR) lpInputString); -#endif - return TRUE; -} // UnicodeToANSI - -VOID -WINAPI -ANSIToUnicode( - LPSTR lpInputString, - LPTSTR lpszOutputString, - int nOutStringLen - ) -{ - -#ifndef WIN32S - CPINFO CodePageInfo; - - lstrcpy(lpszOutputString, (LPTSTR) lpInputString); - - GetCPInfo(CP_ACP, &CodePageInfo); - - if (CodePageInfo.MaxCharSize > 1) - // It must already be a Unicode string - return; - else if (((LPBYTE) lpInputString)[1] != '\0') - { - // Looks like ANSI, better translate it - MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lpInputString, -1, - (LPWSTR) lpszOutputString, nOutStringLen); - } - else - lstrcpy(lpszOutputString, (LPTSTR) lpInputString); -#endif -} // ANSIToUnicode - - -void -MSPrincToMITPrinc( - KERB_EXTERNAL_NAME *msprinc, - WCHAR *realm, - krb5_context context, - krb5_principal *principal - ) -{ - WCHAR princbuf[512],tmpbuf[128]; - char aname[512]; - USHORT i; - princbuf[0]=0; - for (i=0;i<msprinc->NameCount;i++) { - wcsncpy(tmpbuf, msprinc->Names[i].Buffer, - msprinc->Names[i].Length/sizeof(WCHAR)); - tmpbuf[msprinc->Names[i].Length/sizeof(WCHAR)]=0; - if (princbuf[0]) - wcscat(princbuf, L"/"); - wcscat(princbuf, tmpbuf); - } - wcscat(princbuf, L"@"); - wcscat(princbuf, realm); - UnicodeToANSI(princbuf, aname, sizeof(aname)); - krb5_parse_name(context, aname, principal); -} - - -time_t -FileTimeToUnixTime( - LARGE_INTEGER *ltime - ) -{ - FILETIME filetime, localfiletime; - SYSTEMTIME systime; - struct tm utime; - filetime.dwLowDateTime=ltime->LowPart; - filetime.dwHighDateTime=ltime->HighPart; - FileTimeToLocalFileTime(&filetime, &localfiletime); - FileTimeToSystemTime(&localfiletime, &systime); - utime.tm_sec=systime.wSecond; - utime.tm_min=systime.wMinute; - utime.tm_hour=systime.wHour; - utime.tm_mday=systime.wDay; - utime.tm_mon=systime.wMonth-1; - utime.tm_year=systime.wYear-1900; - utime.tm_isdst=-1; - return(mktime(&utime)); -} - -void -MSSessionKeyToMITKeyblock( - KERB_CRYPTO_KEY *mskey, - krb5_context context, - krb5_keyblock *keyblock - ) -{ - krb5_keyblock tmpblock; - tmpblock.magic=KV5M_KEYBLOCK; - tmpblock.enctype=mskey->KeyType; - tmpblock.length=mskey->Length; - tmpblock.contents=mskey->Value; - krb5_copy_keyblock_contents(context, &tmpblock, keyblock); -} - - -void -MSFlagsToMITFlags( - ULONG msflags, - ULONG *mitflags - ) -{ - *mitflags=msflags; -} - -void -MSTicketToMITTicket( - KERB_EXTERNAL_TICKET *msticket, - krb5_context context, - krb5_data *ticket - ) -{ - krb5_data tmpdata, *newdata; - tmpdata.magic=KV5M_DATA; - tmpdata.length=msticket->EncodedTicketSize; - tmpdata.data=msticket->EncodedTicket; - // todo: fix this up a little. this is ugly and will break krb_free_data() - krb5_copy_data(context, &tmpdata, &newdata); - memcpy(ticket, newdata, sizeof(krb5_data)); -} - -void -MSCredToMITCred( - KERB_EXTERNAL_TICKET *msticket, - krb5_context context, - krb5_creds *creds - ) -{ - WCHAR wtmp[128]; - ZeroMemory(creds, sizeof(krb5_creds)); - creds->magic=KV5M_CREDS; - wcsncpy(wtmp, msticket->TargetDomainName.Buffer, - msticket->TargetDomainName.Length/sizeof(WCHAR)); - wtmp[msticket->TargetDomainName.Length/sizeof(WCHAR)]=0; - MSPrincToMITPrinc(msticket->ClientName, wtmp, context, &creds->client); - wcsncpy(wtmp, msticket->DomainName.Buffer, - msticket->DomainName.Length/sizeof(WCHAR)); - wtmp[msticket->DomainName.Length/sizeof(WCHAR)]=0; - MSPrincToMITPrinc(msticket->ServiceName, wtmp, context, &creds->server); - MSSessionKeyToMITKeyblock(&msticket->SessionKey, context, - &creds->keyblock); - MSFlagsToMITFlags(msticket->TicketFlags, &creds->ticket_flags); - creds->times.starttime=FileTimeToUnixTime(&msticket->StartTime); - creds->times.endtime=FileTimeToUnixTime(&msticket->EndTime); - creds->times.renew_till=FileTimeToUnixTime(&msticket->RenewUntil); - - /* MS Tickets are addressless. MIT requires an empty address - * not a NULL list of addresses. - */ - creds->addresses = (krb5_address **)malloc(sizeof(krb5_address *)); - memset(creds->addresses, 0, sizeof(krb5_address *)); - - MSTicketToMITTicket(msticket, context, &creds->ticket); -} - -BOOL -PackageConnectLookup( - HANDLE *pLogonHandle, - ULONG *pPackageId - ) -{ - LSA_STRING Name; - NTSTATUS Status; - - Status = LsaConnectUntrusted( - pLogonHandle - ); - - if (FAILED(Status)) - { - ShowLsaError("LsaConnectUntrusted", Status); - return FALSE; - } - - Name.Buffer = MICROSOFT_KERBEROS_NAME_A; - Name.Length = strlen(Name.Buffer); - Name.MaximumLength = Name.Length + 1; - - Status = LsaLookupAuthenticationPackage( - *pLogonHandle, - &Name, - pPackageId - ); - - if (FAILED(Status)) - { - ShowLsaError("LsaLookupAuthenticationPackage", Status); - return FALSE; - } - - return TRUE; - -} - - -DWORD -ConcatenateUnicodeStrings( - UNICODE_STRING *pTarget, - UNICODE_STRING Source1, - UNICODE_STRING Source2 - ) -{ - // - // The buffers for Source1 and Source2 cannot overlap pTarget's - // buffer. Source1.Length + Source2.Length must be <= 0xFFFF, - // otherwise we overflow... - // - - USHORT TotalSize = Source1.Length + Source2.Length; - PBYTE buffer = (PBYTE) pTarget->Buffer; - - if (TotalSize > pTarget->MaximumLength) - return ERROR_INSUFFICIENT_BUFFER; - - pTarget->Length = TotalSize; - memcpy(buffer, Source1.Buffer, Source1.Length); - memcpy(buffer + Source1.Length, Source2.Buffer, Source2.Length); - return ERROR_SUCCESS; -} - -static BOOL -get_STRING_from_registry( - HKEY hBaseKey, - char * key, - char * value, - char * outbuf, - DWORD outlen - ) -{ - HKEY hKey; - DWORD dwCount; - LONG rc; - - if (!outbuf || outlen == 0) - return 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; - HANDLE TokenHandle; - TOKEN_STATISTICS Stats; - DWORD ReqLen; - BOOL Success; - - if (!ppSessionData) - return FALSE; - *ppSessionData = NULL; - - Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle ); - if ( !Success ) - return FALSE; - - Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen ); - CloseHandle( TokenHandle ); - if ( !Success ) - return FALSE; - - Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData ); - if ( FAILED(Status) || !ppSessionData ) - return FALSE; - - return TRUE; -} - -// -// 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; -} - -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 - // can easily concatenate it later. - // - - TargetPrefix.Buffer = L"krbtgt/"; - TargetPrefix.Length = wcslen(TargetPrefix.Buffer) * sizeof(WCHAR); - TargetPrefix.MaximumLength = TargetPrefix.Length; - - // - // 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 + DomainName.Length; - - // - // The ticket request buffer needs to be a single buffer. That buffer - // needs to include the buffer for the target name. - // - - RequestSize = sizeof(*pTicketRequest) + TargetSize; - - // - // Allocate the request buffer and make sure it's zero-filled. - // - - pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize); - if (!pTicketRequest) - return GetLastError(); - - // - // Concatenate the target prefix with the previous reponse's - // target domain. - // - - pTicketRequest->TargetName.Length = 0; - pTicketRequest->TargetName.MaximumLength = TargetSize; - pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1); - Status = ConcatenateUnicodeStrings(&(pTicketRequest->TargetName), - 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 - ) -{ - // - // 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, MinLife, EndTime, LocalEndTime; - __int64 temp; - // FILETIME is in units of 100 nano-seconds - // If obtained tickets are either expired or have a lifetime - // less than 20 minutes, retry ... - GetSystemTimeAsFileTime(&Now); - EndTime.dwLowDateTime=pTicketResponse->Ticket.EndTime.LowPart; - EndTime.dwHighDateTime=pTicketResponse->Ticket.EndTime.HighPart; - FileTimeToLocalFileTime(&EndTime, &LocalEndTime); - temp = Now.dwHighDateTime; - temp <<= 32; - temp = Now.dwLowDateTime; - temp += 1200 * 10000; - MinLife.dwHighDateTime = (DWORD)((temp >> 32) & 0xFFFFFFFF); - MinLife.dwLowDateTime = (DWORD)(temp & 0xFFFFFFFF); - if (CompareFileTime(&MinLife, &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 -#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 = 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; - } - - - // - // 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; - - if ( pTicketResponse ) { - memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE)); - LsaFreeReturnBuffer(pTicketResponse); - pTicketResponse = NULL; - } - - Status = LsaCallAuthenticationPackage( - LogonHandle, - PackageId, - pTicketRequest, - RequestSize, - &pTicketResponse, - &ResponseSize, - &SubStatus - ); - - if (FAILED(Status) || FAILED(SubStatus)) - { - bIsLsaError = TRUE; - goto cleanup; - } - - cleanup: - if ( pTicketRequest ) - LsaFreeReturnBuffer(pTicketRequest); - - if (FAILED(Status) || FAILED(SubStatus)) - { - if (bIsLsaError) - { - // XXX - Will be fixed later - if (FAILED(Status)) - ShowLsaError("LsaCallAuthenticationPackage", Status); - if (FAILED(SubStatus)) - ShowLsaError("LsaCallAuthenticationPackage", SubStatus); - } - else - { - ShowWinError("GetMSTGT", Status); - } - - if (pTicketResponse) { - memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE)); - LsaFreeReturnBuffer(pTicketResponse); - pTicketResponse = NULL; - } - return(FALSE); - } - - *ticket = &(pTicketResponse->Ticket); - return(TRUE); -} +#include "krb5.h" void main( @@ -849,38 +37,44 @@ main( { krb5_context kcontext; krb5_error_code code; - krb5_creds creds; krb5_ccache ccache=NULL; + krb5_ccache mslsa_ccache=NULL; krb5_get_init_creds_opt opts; - char *cache_name=NULL; - HANDLE LogonHandle=NULL; - ULONG PackageId; + krb5_principal princ; - KERB_EXTERNAL_TICKET *msticket; - if(!PackageConnectLookup(&LogonHandle, &PackageId)) - exit(1); - - if (GetMSTGT(LogonHandle, PackageId, &msticket)==FALSE) - exit(1); if (code = krb5_init_context(&kcontext)) { com_err(argv[0], code, "while initializing kerberos library"); exit(1); } krb5_get_init_creds_opt_init(&opts); - MSCredToMITCred(msticket, kcontext, &creds); + + if (code = krb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache)) { + com_err(argv[0], code, "while opening MS LSA ccache"); + exit(1); + } + + if (code = krb5_cc_get_principal(kcontext, mslsa_ccache, &princ)) { + com_err(argv[0], code, "while obtaining MS LSA principal"); + exit(1); + } + if (code = krb5_cc_default(kcontext, &ccache)) { com_err(argv[0], code, "while getting default ccache"); exit(1); } - if (code = krb5_cc_initialize(kcontext, ccache, creds.client)) { - com_err (argv[0], code, "when initializing cache %s", - cache_name?cache_name:""); + if (code = krb5_cc_initialize(kcontext, ccache, princ)) { + com_err (argv[0], code, "when initializing ccache"); exit(1); } - if (code = krb5_cc_store_cred(kcontext, ccache, &creds)) { - com_err (argv[0], code, "while storing credentials"); + + if (code = krb5_cc_copy_creds(kcontext, mslsa_ccache, ccache)) { + com_err (argv[0], code, "while copying MS LSA ccache to default ccache"); exit(1); } + + krb5_free_principal(kcontext, princ); krb5_cc_close(kcontext, ccache); + krb5_cc_close(kcontext, mslsa_ccache); krb5_free_context(kcontext); + return(0); } |
