summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorJeffrey Altman <jaltman@secure-endpoints.com>2003-12-12 22:22:36 +0000
committerJeffrey Altman <jaltman@secure-endpoints.com>2003-12-12 22:22:36 +0000
commit1024cce992605494dec0566a963efb1bdc0ff1d5 (patch)
tree297fbeb92fb86d8bda2d0df38fb888fc30b6f13a /src/lib
parentf76582cbba8012679b4fd04db4880f09e80988be (diff)
downloadkrb5-1024cce992605494dec0566a963efb1bdc0ff1d5.tar.gz
krb5-1024cce992605494dec0566a963efb1bdc0ff1d5.tar.xz
krb5-1024cce992605494dec0566a963efb1bdc0ff1d5.zip
* Added new krb5_ccache type "MSLSA" for Windows only.
This new ccache type provides an interface for the MIT krb5_cc api functions to be used to access the contents of the MS Kerberos LSA cache. The ccache type is read-only because the MS Kerberos LSA does not allow third party applications to insert credentials into the cache. The primary motivation of this work was to encapsulate the complex operations necessary to manipulate the MS Kerberos LSA. The code was far from trivial and was often implemented incorrectly. Worse still was the fact that each version of Windows since W2K modified the use of the LSA API. The code which was originally donated in the form of ms2mit.c had many memory and handle leaks which were acceptable for a one time application such as ms2mit.c. Unfortunately, this code has started to appear in many other applications: KfW's Leash, the AFS Wake systray tool, and others. By using the new MSLSA ccache the implementation of ms2mit.c went from 890 lines to 50 lines of code and comments. All that is necessary is for the MSLSA ccache to be resolved and for its contents to be copied with krb5_cc_copy_creds to the default ccache. The MSLSA ccache implements all of the functions of a ccache except those which would be used to store data into the ccache. When a write attempt is performed the new error KRB5_CC_READONLY is returned. The residual portion of the MSLSA ccache name is current ignored but preserved. If you ask for ccache "MSLSA:myname" you will be given access to the LSA cache for the current Logon Session. If you later ask for the name of the ccache you will be returned the same name. In the future, the residual might be used to provide information necessary to identify a specific logon session whose cache it is desired to access. If this is ever done, the applications which use it will have to possess the SeTcbPrivilege privilege. Using KfW's Leash it is now possible to set the Krb5 credential cache to "MSLSA:" and use it to monitor the contents of the MS Kerberos LSA cache. As part of adding this functionality, krb5_32.dll is not linked against the "secur32.lib" library as the Lsa security sdk routines are stored in the SECUR32.DLL file. ticket: 2049 target_version: 1.3.2 tags: pullup git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@15886 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ChangeLog5
-rw-r--r--src/lib/Makefile.in2
-rw-r--r--src/lib/krb5/ccache/ChangeLog24
-rw-r--r--src/lib/krb5/ccache/Makefile.in6
-rw-r--r--src/lib/krb5/ccache/cc_mslsa.c1265
-rw-r--r--src/lib/krb5/ccache/ccbase.c8
-rw-r--r--src/lib/krb5/error_tables/ChangeLog4
-rw-r--r--src/lib/krb5/error_tables/krb5_err.et2
8 files changed, 1314 insertions, 2 deletions
diff --git a/src/lib/ChangeLog b/src/lib/ChangeLog
index 6e3b194674..c6777db664 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 01a43511db..030beb7bd2 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 9bfb82facd..d8a5ec94e7 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 937c586f32..6b3e4a4413 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 0000000000..72a2b7fbf4
--- /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 cfe96ec057..f17870663a 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 2eb6925ac5..c5f1371b85 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 66bdebbfb4..622143b7de 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