1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/cc_mslsa.c */
4 * Copyright 2007 Secure Endpoints Inc.
6 * Copyright 2003,2004 by the Massachusetts Institute of Technology.
9 * Export of this software from the United States of America may
10 * require a specific license from the United States Government.
11 * It is the responsibility of any person or organization contemplating
12 * export to obtain such a license before exporting.
14 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15 * distribute this software and its documentation for any purpose and
16 * without fee is hereby granted, provided that the above copyright
17 * notice appear in all copies and that both that copyright notice and
18 * this permission notice appear in supporting documentation, and that
19 * the name of M.I.T. not be used in advertising or publicity pertaining
20 * to distribution of the software without specific, written prior
21 * permission. Furthermore if you modify this software you must label
22 * your software as modified software and not distribute it in such a
23 * fashion that it might be confused with the original M.I.T. software.
24 * M.I.T. makes no representations about the suitability of
25 * this software for any purpose. It is provided "as is" without express
26 * or implied warranty.
28 * Copyright 2000 by Carnegie Mellon University
32 * Permission to use, copy, modify, and distribute this software and its
33 * documentation for any purpose and without fee is hereby granted,
34 * provided that the above copyright notice appear in all copies and that
35 * both that copyright notice and this permission notice appear in
36 * supporting documentation, and that the name of Carnegie Mellon
37 * University not be used in advertising or publicity pertaining to
38 * distribution of the software without specific, written prior
41 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
42 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
43 * FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
44 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
45 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
46 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
47 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49 * Implementation of microsoft windows lsa credentials cache
57 #define WIN32_NO_STATUS
68 #define SECURITY_WIN32
73 #define _WIN32_WINNT 0x0600
77 #define MAX_MSG_SIZE 256
78 #define MAX_MSPRINC_SIZE 1024
81 * The function does_query_ticket_cache_ex2()
82 * contains static variables to cache the responses of the tests being
83 * performed. There is no harm in the test being performed more than
84 * once since the result will always be the same.
87 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
90 ShowWinError(LPSTR szAPI, DWORD dwError)
93 // TODO - Write errors to event log so that scripts that don't
94 // check for errors will still get something in the event log
96 // This code is completely unsafe for use on non-English systems
97 // Any call to this function will result in the FormatMessage
98 // call failing and the program terminating. This might have
99 // been acceptable when this code was part of ms2mit.exe as
100 // a standalone executable but it is not appropriate for a library
103 WCHAR szMsgBuf[MAX_MSG_SIZE];
106 printf("Error calling function %s: %lu\n", szAPI, dwError);
108 dwRes = FormatMessage (
109 FORMAT_MESSAGE_FROM_SYSTEM,
112 MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US),
117 printf("FormatMessage failed with %d\n", GetLastError());
118 ExitProcess(EXIT_FAILURE);
121 printf("%S",szMsgBuf);
126 ShowLsaError(LPSTR szAPI, NTSTATUS Status)
129 // Convert the NTSTATUS to Winerror. Then call ShowWinError().
131 ShowWinError(szAPI, LsaNtStatusToWinError(Status));
136 UnicodeToANSI(LPTSTR lpInputString, LPSTR lpszOutputString, int nOutStringLen)
140 GetCPInfo(CP_ACP, &CodePageInfo);
142 if (CodePageInfo.MaxCharSize > 1) {
143 // Only supporting non-Unicode strings
144 int reqLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) lpInputString, -1,
145 NULL, 0, NULL, NULL);
146 if ( reqLen > nOutStringLen)
150 if (WideCharToMultiByte(CP_ACP,
151 /* WC_NO_BEST_FIT_CHARS | */ WC_COMPOSITECHECK,
152 (LPCWSTR) lpInputString, -1,
154 nOutStringLen, NULL, NULL) == 0)
160 // Looks like unicode, better translate it
161 if (WideCharToMultiByte(CP_ACP,
162 /* WC_NO_BEST_FIT_CHARS | */ WC_COMPOSITECHECK,
163 (LPCWSTR) lpInputString, -1,
165 nOutStringLen, NULL, NULL) == 0)
174 ANSIToUnicode(LPCSTR lpInputString, LPWSTR lpszOutputString, int nOutStringLen)
179 GetCPInfo(CP_ACP, &CodePageInfo);
181 MultiByteToWideChar(CP_ACP, 0, lpInputString, -1,
182 lpszOutputString, nOutStringLen);
187 MITPrincToMSPrinc(krb5_context context, krb5_principal principal, UNICODE_STRING * msprinc)
191 if (!krb5_unparse_name(context, principal, &aname)) {
192 msprinc->Length = strlen(aname) * sizeof(WCHAR);
193 if ( msprinc->Length <= msprinc->MaximumLength )
194 ANSIToUnicode(aname, msprinc->Buffer, msprinc->MaximumLength);
197 krb5_free_unparsed_name(context,aname);
202 UnicodeStringToMITPrinc(UNICODE_STRING *service, UNICODE_STRING *realm,
203 krb5_context context, krb5_principal *principal)
209 /* Convert the realm to a wchar string. */
211 wcsncpy(realmbuf, realm->Buffer, realm->Length / sizeof(WCHAR));
212 realmbuf[realm->Length / sizeof(WCHAR)] = 0;
213 /* Convert the principal components to a wchar string. */
215 wcsncpy(princbuf, service->Buffer, service->Length/sizeof(WCHAR));
216 princbuf[service->Length/sizeof(WCHAR)]=0;
217 wcscat(princbuf, L"@");
218 wcscat(princbuf, realmbuf);
219 if (UnicodeToANSI(princbuf, aname, sizeof(aname))) {
220 if (krb5_parse_name(context, aname, principal) == 0)
228 KerbExternalNameToMITPrinc(KERB_EXTERNAL_NAME *msprinc, WCHAR *realm, krb5_context context,
229 krb5_principal *principal)
231 WCHAR princbuf[512],tmpbuf[128];
235 for (i=0;i<msprinc->NameCount;i++) {
236 wcsncpy(tmpbuf, msprinc->Names[i].Buffer,
237 msprinc->Names[i].Length/sizeof(WCHAR));
238 tmpbuf[msprinc->Names[i].Length/sizeof(WCHAR)]=0;
240 wcscat(princbuf, L"/");
241 wcscat(princbuf, tmpbuf);
243 wcscat(princbuf, L"@");
244 wcscat(princbuf, realm);
245 if (UnicodeToANSI(princbuf, aname, sizeof(aname))) {
246 if (krb5_parse_name(context, aname, principal) == 0)
253 FileTimeToUnixTime(LARGE_INTEGER *ltime)
255 FILETIME filetime, localfiletime;
258 filetime.dwLowDateTime=ltime->LowPart;
259 filetime.dwHighDateTime=ltime->HighPart;
260 FileTimeToLocalFileTime(&filetime, &localfiletime);
261 FileTimeToSystemTime(&localfiletime, &systime);
262 utime.tm_sec=systime.wSecond;
263 utime.tm_min=systime.wMinute;
264 utime.tm_hour=systime.wHour;
265 utime.tm_mday=systime.wDay;
266 utime.tm_mon=systime.wMonth-1;
267 utime.tm_year=systime.wYear-1900;
269 return(mktime(&utime));
273 MSSessionKeyToMITKeyblock(KERB_CRYPTO_KEY *mskey, krb5_context context, krb5_keyblock *keyblock)
275 krb5_keyblock tmpblock;
276 tmpblock.magic=KV5M_KEYBLOCK;
277 tmpblock.enctype=mskey->KeyType;
278 tmpblock.length=mskey->Length;
279 tmpblock.contents=mskey->Value;
280 krb5_copy_keyblock_contents(context, &tmpblock, keyblock);
284 IsMSSessionKeyNull(KERB_CRYPTO_KEY *mskey)
288 if (mskey->KeyType == KERB_ETYPE_NULL)
291 for ( i=0; i<mskey->Length; i++ ) {
300 MSFlagsToMITFlags(ULONG msflags, ULONG *mitflags)
306 MSTicketToMITTicket(KERB_EXTERNAL_TICKET *msticket, krb5_context context, krb5_data *ticket)
308 krb5_data tmpdata, *newdata = 0;
311 tmpdata.magic=KV5M_DATA;
312 tmpdata.length=msticket->EncodedTicketSize;
313 tmpdata.data=msticket->EncodedTicket;
315 // this is ugly and will break krb5_free_data()
316 // now that this is being done within the library it won't break krb5_free_data()
317 rc = krb5_copy_data(context, &tmpdata, &newdata);
321 memcpy(ticket, newdata, sizeof(krb5_data));
327 MSCredToMITCred(KERB_EXTERNAL_TICKET *msticket, UNICODE_STRING ClientRealm,
328 krb5_context context, krb5_creds *creds)
331 ZeroMemory(creds, sizeof(krb5_creds));
332 creds->magic=KV5M_CREDS;
334 // construct Client Principal
335 wcsncpy(wrealm, ClientRealm.Buffer, ClientRealm.Length/sizeof(WCHAR));
336 wrealm[ClientRealm.Length/sizeof(WCHAR)]=0;
337 if (!KerbExternalNameToMITPrinc(msticket->ClientName, wrealm, context, &creds->client))
340 // construct Service Principal
341 wcsncpy(wrealm, msticket->DomainName.Buffer,
342 msticket->DomainName.Length/sizeof(WCHAR));
343 wrealm[msticket->DomainName.Length/sizeof(WCHAR)]=0;
344 if (!KerbExternalNameToMITPrinc(msticket->ServiceName, wrealm, context, &creds->server))
346 MSSessionKeyToMITKeyblock(&msticket->SessionKey, context,
348 MSFlagsToMITFlags(msticket->TicketFlags, &creds->ticket_flags);
349 creds->times.starttime=FileTimeToUnixTime(&msticket->StartTime);
350 creds->times.endtime=FileTimeToUnixTime(&msticket->EndTime);
351 creds->times.renew_till=FileTimeToUnixTime(&msticket->RenewUntil);
353 creds->addresses = NULL;
355 return MSTicketToMITTicket(msticket, context, &creds->ticket);
358 /* CacheInfoEx2ToMITCred is used when we do not need the real ticket */
360 CacheInfoEx2ToMITCred(KERB_TICKET_CACHE_INFO_EX2 *info,
361 krb5_context context, krb5_creds *creds)
363 ZeroMemory(creds, sizeof(krb5_creds));
364 creds->magic=KV5M_CREDS;
366 // construct Client Principal
367 if (!UnicodeStringToMITPrinc(&info->ClientName, &info->ClientRealm,
368 context, &creds->client))
371 // construct Service Principal
372 if (!UnicodeStringToMITPrinc(&info->ServerName, &info->ServerRealm,
373 context, &creds->server))
376 creds->keyblock.magic = KV5M_KEYBLOCK;
377 creds->keyblock.enctype = info->SessionKeyType;
378 creds->ticket_flags = info->TicketFlags;
379 MSFlagsToMITFlags(info->TicketFlags, &creds->ticket_flags);
380 creds->times.starttime=FileTimeToUnixTime(&info->StartTime);
381 creds->times.endtime=FileTimeToUnixTime(&info->EndTime);
382 creds->times.renew_till=FileTimeToUnixTime(&info->RenewTime);
384 /* MS Tickets are addressless. MIT requires an empty address
385 * not a NULL list of addresses.
387 creds->addresses = (krb5_address **)malloc(sizeof(krb5_address *));
388 memset(creds->addresses, 0, sizeof(krb5_address *));
394 PackageConnectLookup(HANDLE *pLogonHandle, ULONG *pPackageId)
399 Status = LsaConnectUntrusted(
405 ShowLsaError("LsaConnectUntrusted", Status);
409 Name.Buffer = MICROSOFT_KERBEROS_NAME_A;
410 Name.Length = strlen(Name.Buffer);
411 Name.MaximumLength = Name.Length + 1;
413 Status = LsaLookupAuthenticationPackage(
421 ShowLsaError("LsaLookupAuthenticationPackage", Status);
430 * This runtime check is only needed on Windows XP and Server 2003.
431 * It can safely be removed when we no longer wish to support any
432 * versions of those platforms.
435 does_query_ticket_cache_ex2 (void)
437 static BOOL fChecked = FALSE;
438 static BOOL fEx2Response = FALSE;
443 NTSTATUS SubStatus = 0;
447 PKERB_QUERY_TKT_CACHE_REQUEST pCacheRequest = NULL;
448 PKERB_QUERY_TKT_CACHE_EX2_RESPONSE pCacheResponse = NULL;
451 RequestSize = sizeof(*pCacheRequest) + 1;
453 if (!PackageConnectLookup(&LogonHandle, &PackageId))
456 pCacheRequest = (PKERB_QUERY_TKT_CACHE_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize);
457 if (!pCacheRequest) {
458 LsaDeregisterLogonProcess(LogonHandle);
462 pCacheRequest->MessageType = KerbQueryTicketCacheEx2Message;
463 pCacheRequest->LogonId.LowPart = 0;
464 pCacheRequest->LogonId.HighPart = 0;
466 Status = LsaCallAuthenticationPackage( LogonHandle,
475 LocalFree(pCacheRequest);
476 LsaDeregisterLogonProcess(LogonHandle);
478 if (!(FAILED(Status) || FAILED(SubStatus))) {
479 LsaFreeReturnBuffer(pCacheResponse);
489 ConcatenateUnicodeStrings(UNICODE_STRING *pTarget, UNICODE_STRING Source1, UNICODE_STRING Source2)
492 // The buffers for Source1 and Source2 cannot overlap pTarget's
493 // buffer. Source1.Length + Source2.Length must be <= 0xFFFF,
494 // otherwise we overflow...
497 USHORT TotalSize = Source1.Length + Source2.Length;
498 PBYTE buffer = (PBYTE) pTarget->Buffer;
500 if (TotalSize > pTarget->MaximumLength)
501 return ERROR_INSUFFICIENT_BUFFER;
503 if ( pTarget->Buffer != Source1.Buffer )
504 memcpy(buffer, Source1.Buffer, Source1.Length);
505 memcpy(buffer + Source1.Length, Source2.Buffer, Source2.Length);
507 pTarget->Length = TotalSize;
508 return ERROR_SUCCESS;
512 get_STRING_from_registry(HKEY hBaseKey, char * key, char * value, char * outbuf, DWORD outlen)
518 if (!outbuf || outlen == 0)
521 rc = RegOpenKeyExA(hBaseKey, key, 0, KEY_QUERY_VALUE, &hKey);
526 rc = RegQueryValueExA(hKey, value, 0, 0, (LPBYTE) outbuf, &dwCount);
529 return rc?FALSE:TRUE;
533 GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)
537 TOKEN_STATISTICS Stats;
543 *ppSessionData = NULL;
545 Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );
549 Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
550 CloseHandle( TokenHandle );
554 Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
555 if ( FAILED(Status) || !ppSessionData )
562 ConstructTicketRequest(UNICODE_STRING DomainName, PKERB_RETRIEVE_TKT_REQUEST * outRequest, ULONG * outSize)
565 UNICODE_STRING TargetPrefix;
568 PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
574 // Set up the "krbtgt/" target prefix into a UNICODE_STRING so we
575 // can easily concatenate it later.
578 TargetPrefix.Buffer = L"krbtgt/";
579 TargetPrefix.Length = wcslen(TargetPrefix.Buffer) * sizeof(WCHAR);
580 TargetPrefix.MaximumLength = TargetPrefix.Length;
583 // We will need to concatenate the "krbtgt/" prefix and the
584 // Logon Session's DnsDomainName into our request's target name.
586 // Therefore, first compute the necessary buffer size for that.
588 // Note that we might theoretically have integer overflow.
591 TargetSize = TargetPrefix.Length + DomainName.Length;
594 // The ticket request buffer needs to be a single buffer. That buffer
595 // needs to include the buffer for the target name.
598 RequestSize = sizeof(*pTicketRequest) + TargetSize;
601 // Allocate the request buffer and make sure it's zero-filled.
604 pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize);
606 return GetLastError();
609 // Concatenate the target prefix with the previous reponse's
613 pTicketRequest->TargetName.Length = 0;
614 pTicketRequest->TargetName.MaximumLength = TargetSize;
615 pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
616 Error = ConcatenateUnicodeStrings(&(pTicketRequest->TargetName),
619 *outRequest = pTicketRequest;
620 *outSize = RequestSize;
625 PurgeAllTickets(HANDLE LogonHandle, ULONG PackageId)
628 NTSTATUS SubStatus = 0;
629 KERB_PURGE_TKT_CACHE_REQUEST PurgeRequest;
631 PurgeRequest.MessageType = KerbPurgeTicketCacheMessage;
632 PurgeRequest.LogonId.LowPart = 0;
633 PurgeRequest.LogonId.HighPart = 0;
634 PurgeRequest.ServerName.Buffer = L"";
635 PurgeRequest.ServerName.Length = 0;
636 PurgeRequest.ServerName.MaximumLength = 0;
637 PurgeRequest.RealmName.Buffer = L"";
638 PurgeRequest.RealmName.Length = 0;
639 PurgeRequest.RealmName.MaximumLength = 0;
640 Status = LsaCallAuthenticationPackage(LogonHandle,
643 sizeof(PurgeRequest),
648 if (FAILED(Status) || FAILED(SubStatus))
654 PurgeTicketEx(HANDLE LogonHandle, ULONG PackageId,
655 krb5_context context, krb5_flags flags, krb5_creds *cred)
658 NTSTATUS SubStatus = 0;
659 KERB_PURGE_TKT_CACHE_EX_REQUEST * pPurgeRequest;
660 DWORD dwRequestLen = sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 4096;
661 char * cname = NULL, * crealm = NULL;
662 char * sname = NULL, * srealm = NULL;
664 if (krb5_unparse_name(context, cred->client, &cname))
667 if (krb5_unparse_name(context, cred->server, &sname)) {
668 krb5_free_unparsed_name(context, cname);
672 pPurgeRequest = malloc(dwRequestLen);
673 if ( pPurgeRequest == NULL )
675 memset(pPurgeRequest, 0, dwRequestLen);
677 crealm = strrchr(cname, '@');
681 srealm = strrchr(sname, '@');
685 pPurgeRequest->MessageType = KerbPurgeTicketCacheExMessage;
686 pPurgeRequest->LogonId.LowPart = 0;
687 pPurgeRequest->LogonId.HighPart = 0;
688 pPurgeRequest->Flags = 0;
689 pPurgeRequest->TicketTemplate.ClientName.Buffer = (PWSTR)((CHAR *)pPurgeRequest + sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST));
690 pPurgeRequest->TicketTemplate.ClientName.Length = strlen(cname)*sizeof(WCHAR);
691 pPurgeRequest->TicketTemplate.ClientName.MaximumLength = 256;
692 ANSIToUnicode(cname, pPurgeRequest->TicketTemplate.ClientName.Buffer,
693 pPurgeRequest->TicketTemplate.ClientName.MaximumLength);
695 pPurgeRequest->TicketTemplate.ClientRealm.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 512);
696 pPurgeRequest->TicketTemplate.ClientRealm.Length = strlen(crealm)*sizeof(WCHAR);
697 pPurgeRequest->TicketTemplate.ClientRealm.MaximumLength = 256;
698 ANSIToUnicode(crealm, pPurgeRequest->TicketTemplate.ClientRealm.Buffer,
699 pPurgeRequest->TicketTemplate.ClientRealm.MaximumLength);
701 pPurgeRequest->TicketTemplate.ServerName.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 1024);
702 pPurgeRequest->TicketTemplate.ServerName.Length = strlen(sname)*sizeof(WCHAR);
703 pPurgeRequest->TicketTemplate.ServerName.MaximumLength = 256;
704 ANSIToUnicode(sname, pPurgeRequest->TicketTemplate.ServerName.Buffer,
705 pPurgeRequest->TicketTemplate.ServerName.MaximumLength);
707 pPurgeRequest->TicketTemplate.ServerRealm.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 1536);
708 pPurgeRequest->TicketTemplate.ServerRealm.Length = strlen(srealm)*sizeof(WCHAR);
709 pPurgeRequest->TicketTemplate.ServerRealm.MaximumLength = 256;
710 ANSIToUnicode(srealm, pPurgeRequest->TicketTemplate.ServerRealm.Buffer,
711 pPurgeRequest->TicketTemplate.ServerRealm.MaximumLength);
713 pPurgeRequest->TicketTemplate.StartTime;
714 pPurgeRequest->TicketTemplate.EndTime;
715 pPurgeRequest->TicketTemplate.RenewTime;
716 pPurgeRequest->TicketTemplate.EncryptionType = cred->keyblock.enctype;
717 pPurgeRequest->TicketTemplate.TicketFlags = flags;
719 Status = LsaCallAuthenticationPackage( LogonHandle,
728 krb5_free_unparsed_name(context,cname);
729 krb5_free_unparsed_name(context,sname);
731 if (FAILED(Status) || FAILED(SubStatus))
737 KerbSubmitTicket( HANDLE LogonHandle, ULONG PackageId,
738 krb5_context context, krb5_creds *cred)
741 NTSTATUS SubStatus = 0;
742 KERB_SUBMIT_TKT_REQUEST * pSubmitRequest;
744 krb5_auth_context auth_context;
745 krb5_keyblock * keyblock = 0;
746 krb5_replay_data replaydata;
747 krb5_data * krb_cred = 0;
750 if (krb5_auth_con_init(context, &auth_context)) {
754 if (krb5_auth_con_setflags(context, auth_context,
755 KRB5_AUTH_CONTEXT_RET_TIME)) {
759 krb5_auth_con_getsendsubkey(context, auth_context, &keyblock);
760 if (keyblock == NULL)
761 krb5_auth_con_getkey(context, auth_context, &keyblock);
763 /* make up a key, any key, that can be used to generate the
764 * encrypted KRB_CRED pdu. The Vista release LSA requires
765 * that an enctype other than NULL be used. */
766 if (keyblock == NULL) {
767 keyblock = (krb5_keyblock *)malloc(sizeof(krb5_keyblock));
768 keyblock->enctype = ENCTYPE_ARCFOUR_HMAC;
769 keyblock->length = 16;
770 keyblock->contents = (krb5_octet *)malloc(16);
771 keyblock->contents[0] = 0xde;
772 keyblock->contents[1] = 0xad;
773 keyblock->contents[2] = 0xbe;
774 keyblock->contents[3] = 0xef;
775 keyblock->contents[4] = 0xfe;
776 keyblock->contents[5] = 0xed;
777 keyblock->contents[6] = 0xf0;
778 keyblock->contents[7] = 0xd;
779 keyblock->contents[8] = 0xde;
780 keyblock->contents[9] = 0xad;
781 keyblock->contents[10] = 0xbe;
782 keyblock->contents[11] = 0xef;
783 keyblock->contents[12] = 0xfe;
784 keyblock->contents[13] = 0xed;
785 keyblock->contents[14] = 0xf0;
786 keyblock->contents[15] = 0xd;
787 krb5_auth_con_setsendsubkey(context, auth_context, keyblock);
789 rc = krb5_mk_1cred(context, auth_context, cred, &krb_cred, &replaydata);
791 krb5_auth_con_free(context, auth_context);
793 krb5_free_keyblock(context, keyblock);
795 krb5_free_data(context, krb_cred);
799 dwRequestLen = sizeof(KERB_SUBMIT_TKT_REQUEST) + krb_cred->length + (keyblock ? keyblock->length : 0);
801 pSubmitRequest = (PKERB_SUBMIT_TKT_REQUEST)malloc(dwRequestLen);
802 memset(pSubmitRequest, 0, dwRequestLen);
804 pSubmitRequest->MessageType = KerbSubmitTicketMessage;
805 pSubmitRequest->LogonId.LowPart = 0;
806 pSubmitRequest->LogonId.HighPart = 0;
807 pSubmitRequest->Flags = 0;
810 pSubmitRequest->Key.KeyType = keyblock->enctype;
811 pSubmitRequest->Key.Length = keyblock->length;
812 pSubmitRequest->Key.Offset = sizeof(KERB_SUBMIT_TKT_REQUEST)+krb_cred->length;
814 pSubmitRequest->Key.KeyType = ENCTYPE_NULL;
815 pSubmitRequest->Key.Length = 0;
816 pSubmitRequest->Key.Offset = 0;
818 pSubmitRequest->KerbCredSize = krb_cred->length;
819 pSubmitRequest->KerbCredOffset = sizeof(KERB_SUBMIT_TKT_REQUEST);
820 memcpy(((CHAR *)pSubmitRequest)+sizeof(KERB_SUBMIT_TKT_REQUEST),
821 krb_cred->data, krb_cred->length);
823 memcpy(((CHAR *)pSubmitRequest)+sizeof(KERB_SUBMIT_TKT_REQUEST)+krb_cred->length,
824 keyblock->contents, keyblock->length);
825 krb5_free_data(context, krb_cred);
827 Status = LsaCallAuthenticationPackage( LogonHandle,
835 free(pSubmitRequest);
837 krb5_free_keyblock(context, keyblock);
838 krb5_auth_con_free(context, auth_context);
840 if (FAILED(Status) || FAILED(SubStatus)) {
847 * A simple function to determine if there is an exact match between two tickets
848 * We rely on the fact that the external tickets contain the raw Kerberos ticket.
849 * If the EncodedTicket fields match, the KERB_EXTERNAL_TICKETs must be the same.
852 KerbExternalTicketMatch( PKERB_EXTERNAL_TICKET one, PKERB_EXTERNAL_TICKET two )
854 if ( one->EncodedTicketSize != two->EncodedTicketSize )
857 if ( memcmp(one->EncodedTicket, two->EncodedTicket, one->EncodedTicketSize) )
864 krb5_is_permitted_tgs_enctype(krb5_context context, krb5_const_principal princ, krb5_enctype etype)
866 krb5_enctype *list, *ptr;
869 if (krb5_get_tgs_ktypes(context, princ, &list))
874 for (ptr = list; *ptr; ptr++)
878 krb5_free_enctypes(context, list);
883 // to allow the purging of expired tickets from LSA cache. This is necessary
884 // to force the retrieval of new TGTs. Microsoft does not appear to retrieve
885 // new tickets when they expire. Instead they continue to accept the expired
886 // tickets. This is safe to do because the LSA purges its cache when it
887 // retrieves a new TGT (ms calls this renew) but not when it renews the TGT
888 // (ms calls this refresh).
889 // UAC-limited processes are not allowed to obtain a copy of the MSTGT
890 // session key. We used to check for UAC-limited processes and refuse all
891 // access to the TGT, but this makes the MSLSA ccache completely unusable.
892 // Instead we ought to just flag that the tgt session key is not valid.
895 GetMSTGT(krb5_context context, HANDLE LogonHandle, ULONG PackageId, KERB_EXTERNAL_TICKET **ticket, BOOL enforce_tgs_enctypes)
900 // (FAILED(Status) || FAILED(SubStatus)) ==> error
901 // bIsLsaError ==> LsaCallAuthenticationPackage() error
904 BOOL bIsLsaError = FALSE;
906 NTSTATUS SubStatus = 0;
909 KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
910 PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
911 PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
915 int ignore_cache = 0;
916 krb5_enctype *etype_list = NULL, *ptr = NULL, etype = 0;
918 memset(&CacheRequest, 0, sizeof(KERB_QUERY_TKT_CACHE_REQUEST));
919 CacheRequest.MessageType = KerbRetrieveTicketMessage;
920 CacheRequest.LogonId.LowPart = 0;
921 CacheRequest.LogonId.HighPart = 0;
923 Status = LsaCallAuthenticationPackage(
927 sizeof(CacheRequest),
935 // if the call to LsaCallAuthenticationPackage failed we cannot
936 // perform any queries most likely because the Kerberos package
937 // is not available or we do not have access
942 if (FAILED(SubStatus)) {
943 PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;
944 BOOL Success = FALSE;
945 OSVERSIONINFOEX verinfo;
948 // SubStatus 0x8009030E is not documented. However, it appears
949 // to mean there is no TGT
950 if (SubStatus != 0x8009030E) {
955 verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
956 GetVersionEx((OSVERSIONINFO *)&verinfo);
957 supported = (verinfo.dwMajorVersion > 5) ||
958 (verinfo.dwMajorVersion == 5 && verinfo.dwMinorVersion >= 1);
960 // If we could not get a TGT from the cache we won't know what the
961 // Kerberos Domain should have been. On Windows XP and 2003 Server
962 // we can extract it from the Security Logon Session Data. However,
963 // the required fields are not supported on Windows 2000. :(
964 if ( supported && GetSecurityLogonSessionData(&pSessionData) ) {
965 if ( pSessionData->DnsDomainName.Buffer ) {
966 Error = ConstructTicketRequest(pSessionData->DnsDomainName,
967 &pTicketRequest, &RequestSize);
968 LsaFreeReturnBuffer(pSessionData);
972 LsaFreeReturnBuffer(pSessionData);
977 CHAR UserDnsDomain[256];
978 WCHAR UnicodeUserDnsDomain[256];
979 UNICODE_STRING wrapper;
980 if ( !get_STRING_from_registry(HKEY_CURRENT_USER,
981 "Volatile Environment",
984 sizeof(UserDnsDomain)
990 ANSIToUnicode(UserDnsDomain,UnicodeUserDnsDomain,256);
991 wrapper.Buffer = UnicodeUserDnsDomain;
992 wrapper.Length = wcslen(UnicodeUserDnsDomain) * sizeof(WCHAR);
993 wrapper.MaximumLength = 256;
995 Error = ConstructTicketRequest(wrapper,
996 &pTicketRequest, &RequestSize);
1001 /* We have succeeded in obtaining a credential from the cache.
1002 * Assuming the enctype is one that we support and the ticket
1003 * has not expired and is not marked invalid we will use it.
1004 * Otherwise, we must create a new ticket request and obtain
1005 * a credential we can use.
1008 /* Check Supported Enctypes */
1009 if ( !enforce_tgs_enctypes ||
1010 IsMSSessionKeyNull(&pTicketResponse->Ticket.SessionKey) ||
1011 krb5_is_permitted_tgs_enctype(context, NULL, pTicketResponse->Ticket.SessionKey.KeyType) ) {
1012 FILETIME Now, MinLife, EndTime, LocalEndTime;
1014 // FILETIME is in units of 100 nano-seconds
1015 // If obtained tickets are either expired or have a lifetime
1016 // less than 20 minutes, retry ...
1017 GetSystemTimeAsFileTime(&Now);
1018 EndTime.dwLowDateTime=pTicketResponse->Ticket.EndTime.LowPart;
1019 EndTime.dwHighDateTime=pTicketResponse->Ticket.EndTime.HighPart;
1020 FileTimeToLocalFileTime(&EndTime, &LocalEndTime);
1021 temp = Now.dwHighDateTime;
1023 temp = Now.dwLowDateTime;
1024 temp += 1200 * 10000;
1025 MinLife.dwHighDateTime = (DWORD)((temp >> 32) & 0xFFFFFFFF);
1026 MinLife.dwLowDateTime = (DWORD)(temp & 0xFFFFFFFF);
1027 if (CompareFileTime(&MinLife, &LocalEndTime) >= 0) {
1030 if (pTicketResponse->Ticket.TicketFlags & KERB_TICKET_FLAGS_invalid) {
1031 ignore_cache = 1; // invalid, need to attempt a TGT request
1033 goto cleanup; // we have a valid ticket, all done
1039 Error = ConstructTicketRequest(pTicketResponse->Ticket.TargetDomainName,
1040 &pTicketRequest, &RequestSize);
1046 // Free the previous response buffer so we can get the new response.
1049 if ( pTicketResponse ) {
1050 memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE));
1051 LsaFreeReturnBuffer(pTicketResponse);
1052 pTicketResponse = NULL;
1055 if ( purge_cache ) {
1057 // Purge the existing tickets which we cannot use so new ones can
1058 // be requested. It is not possible to purge just the TGT. All
1059 // service tickets must be purged.
1061 PurgeAllTickets(LogonHandle, PackageId);
1066 // Intialize the request of the request.
1069 pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
1070 pTicketRequest->LogonId.LowPart = 0;
1071 pTicketRequest->LogonId.HighPart = 0;
1072 // Note: pTicketRequest->TargetName set up above
1073 pTicketRequest->CacheOptions = ((ignore_cache || !purge_cache) ?
1074 KERB_RETRIEVE_TICKET_DONT_USE_CACHE : 0L);
1075 pTicketRequest->TicketFlags = 0L;
1076 pTicketRequest->EncryptionType = 0L;
1078 Status = LsaCallAuthenticationPackage( LogonHandle,
1087 if (FAILED(Status) || FAILED(SubStatus))
1094 // Check to make sure the new tickets we received are of a type we support
1097 /* Check Supported Enctypes */
1098 if ( !enforce_tgs_enctypes ||
1099 krb5_is_permitted_tgs_enctype(context, NULL, pTicketResponse->Ticket.SessionKey.KeyType) ) {
1100 goto cleanup; // we have a valid ticket, all done
1103 if (krb5_get_tgs_ktypes(context, NULL, &etype_list)) {
1104 ptr = etype_list = NULL;
1105 etype = ENCTYPE_DES_CBC_CRC;
1107 ptr = etype_list + 1;
1108 etype = *etype_list;
1112 // Try once more but this time specify the Encryption Type
1113 // (This will not store the retrieved tickets in the LSA cache unless
1115 pTicketRequest->EncryptionType = etype;
1116 pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_CACHE_TICKET;
1118 if ( pTicketResponse ) {
1119 memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE));
1120 LsaFreeReturnBuffer(pTicketResponse);
1121 pTicketResponse = NULL;
1124 Status = LsaCallAuthenticationPackage( LogonHandle,
1133 if (FAILED(Status) || FAILED(SubStatus))
1139 if ( pTicketResponse->Ticket.SessionKey.KeyType == etype &&
1140 (!enforce_tgs_enctypes ||
1141 krb5_is_permitted_tgs_enctype(context, NULL, pTicketResponse->Ticket.SessionKey.KeyType)) ) {
1142 goto cleanup; // we have a valid ticket, all done
1154 krb5_free_enctypes(context, etype_list);
1156 if ( pTicketRequest )
1157 LocalFree(pTicketRequest);
1159 if (FAILED(Status) || FAILED(SubStatus))
1163 // XXX - Will be fixed later
1165 ShowLsaError("LsaCallAuthenticationPackage", Status);
1166 if (FAILED(SubStatus))
1167 ShowLsaError("LsaCallAuthenticationPackage", SubStatus);
1171 ShowWinError("GetMSTGT", Status);
1174 if (pTicketResponse) {
1175 memset(pTicketResponse,0,sizeof(KERB_RETRIEVE_TKT_RESPONSE));
1176 LsaFreeReturnBuffer(pTicketResponse);
1177 pTicketResponse = NULL;
1182 *ticket = &(pTicketResponse->Ticket);
1187 GetQueryTktCacheResponseEx(HANDLE LogonHandle, ULONG PackageId,
1188 PKERB_QUERY_TKT_CACHE_EX_RESPONSE * ppResponse)
1190 NTSTATUS Status = 0;
1191 NTSTATUS SubStatus = 0;
1193 KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
1194 PKERB_QUERY_TKT_CACHE_EX_RESPONSE pQueryResponse = NULL;
1197 CacheRequest.MessageType = KerbQueryTicketCacheExMessage;
1198 CacheRequest.LogonId.LowPart = 0;
1199 CacheRequest.LogonId.HighPart = 0;
1201 Status = LsaCallAuthenticationPackage(
1205 sizeof(CacheRequest),
1211 if ( !(FAILED(Status) || FAILED(SubStatus)) ) {
1212 *ppResponse = pQueryResponse;
1220 GetQueryTktCacheResponseEx2(HANDLE LogonHandle, ULONG PackageId,
1221 PKERB_QUERY_TKT_CACHE_EX2_RESPONSE * ppResponse)
1223 NTSTATUS Status = 0;
1224 NTSTATUS SubStatus = 0;
1226 KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
1227 PKERB_QUERY_TKT_CACHE_EX2_RESPONSE pQueryResponse = NULL;
1230 CacheRequest.MessageType = KerbQueryTicketCacheEx2Message;
1231 CacheRequest.LogonId.LowPart = 0;
1232 CacheRequest.LogonId.HighPart = 0;
1234 Status = LsaCallAuthenticationPackage(
1238 sizeof(CacheRequest),
1244 if ( !(FAILED(Status) || FAILED(SubStatus)) ) {
1245 *ppResponse = pQueryResponse;
1253 GetMSCacheTicketFromMITCred( HANDLE LogonHandle, ULONG PackageId,
1254 krb5_context context, krb5_creds *creds,
1255 PKERB_EXTERNAL_TICKET *ticket)
1257 NTSTATUS Status = 0;
1258 NTSTATUS SubStatus = 0;
1260 PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
1261 PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
1264 RequestSize = sizeof(*pTicketRequest) + MAX_MSPRINC_SIZE;
1266 pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize);
1267 if (!pTicketRequest)
1270 pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
1271 pTicketRequest->LogonId.LowPart = 0;
1272 pTicketRequest->LogonId.HighPart = 0;
1274 pTicketRequest->TargetName.Length = 0;
1275 pTicketRequest->TargetName.MaximumLength = MAX_MSPRINC_SIZE;
1276 pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
1277 MITPrincToMSPrinc(context, creds->server, &pTicketRequest->TargetName);
1278 pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_CACHE_TICKET;
1279 pTicketRequest->TicketFlags = creds->ticket_flags;
1280 pTicketRequest->EncryptionType = creds->keyblock.enctype;
1282 Status = LsaCallAuthenticationPackage( LogonHandle,
1291 LocalFree(pTicketRequest);
1293 if (FAILED(Status) || FAILED(SubStatus))
1296 /* otherwise return ticket */
1297 *ticket = &(pTicketResponse->Ticket);
1302 GetMSCacheTicketFromCacheInfoEx(HANDLE LogonHandle, ULONG PackageId,
1303 PKERB_TICKET_CACHE_INFO_EX tktinfo,
1304 PKERB_EXTERNAL_TICKET *ticket)
1306 NTSTATUS Status = 0;
1307 NTSTATUS SubStatus = 0;
1309 PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
1310 PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
1313 RequestSize = sizeof(*pTicketRequest) + tktinfo->ServerName.Length;
1315 pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize);
1316 if (!pTicketRequest)
1319 pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
1320 pTicketRequest->LogonId.LowPart = 0;
1321 pTicketRequest->LogonId.HighPart = 0;
1322 pTicketRequest->TargetName.Length = tktinfo->ServerName.Length;
1323 pTicketRequest->TargetName.MaximumLength = tktinfo->ServerName.Length;
1324 pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
1325 memcpy(pTicketRequest->TargetName.Buffer,tktinfo->ServerName.Buffer, tktinfo->ServerName.Length);
1326 pTicketRequest->CacheOptions = 0;
1327 pTicketRequest->EncryptionType = tktinfo->EncryptionType;
1328 pTicketRequest->TicketFlags = 0;
1329 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_forwardable )
1330 pTicketRequest->TicketFlags |= KDC_OPT_FORWARDABLE;
1331 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_forwarded )
1332 pTicketRequest->TicketFlags |= KDC_OPT_FORWARDED;
1333 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_proxiable )
1334 pTicketRequest->TicketFlags |= KDC_OPT_PROXIABLE;
1335 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_renewable )
1336 pTicketRequest->TicketFlags |= KDC_OPT_RENEWABLE;
1338 Status = LsaCallAuthenticationPackage(
1348 LocalFree(pTicketRequest);
1350 if (FAILED(Status) || FAILED(SubStatus))
1353 /* otherwise return ticket */
1354 *ticket = &(pTicketResponse->Ticket);
1356 /* set the initial flag if we were attempting to retrieve one
1357 * because Windows won't necessarily return the initial ticket
1360 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_initial )
1361 (*ticket)->TicketFlags |= KERB_TICKET_FLAGS_initial;
1367 GetMSCacheTicketFromCacheInfoEx2(HANDLE LogonHandle, ULONG PackageId,
1368 PKERB_TICKET_CACHE_INFO_EX2 tktinfo,
1369 PKERB_EXTERNAL_TICKET *ticket)
1371 NTSTATUS Status = 0;
1372 NTSTATUS SubStatus = 0;
1374 PKERB_RETRIEVE_TKT_REQUEST pTicketRequest = NULL;
1375 PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
1378 RequestSize = sizeof(*pTicketRequest) + tktinfo->ServerName.Length;
1380 pTicketRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LMEM_ZEROINIT, RequestSize);
1381 if (!pTicketRequest)
1384 pTicketRequest->MessageType = KerbRetrieveEncodedTicketMessage;
1385 pTicketRequest->LogonId.LowPart = 0;
1386 pTicketRequest->LogonId.HighPart = 0;
1387 pTicketRequest->TargetName.Length = tktinfo->ServerName.Length;
1388 pTicketRequest->TargetName.MaximumLength = tktinfo->ServerName.Length;
1389 pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
1390 memcpy(pTicketRequest->TargetName.Buffer,tktinfo->ServerName.Buffer, tktinfo->ServerName.Length);
1391 pTicketRequest->CacheOptions = KERB_RETRIEVE_TICKET_CACHE_TICKET;
1392 pTicketRequest->EncryptionType = tktinfo->SessionKeyType;
1393 pTicketRequest->TicketFlags = 0;
1394 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_forwardable )
1395 pTicketRequest->TicketFlags |= KDC_OPT_FORWARDABLE;
1396 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_forwarded )
1397 pTicketRequest->TicketFlags |= KDC_OPT_FORWARDED;
1398 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_proxiable )
1399 pTicketRequest->TicketFlags |= KDC_OPT_PROXIABLE;
1400 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_renewable )
1401 pTicketRequest->TicketFlags |= KDC_OPT_RENEWABLE;
1403 Status = LsaCallAuthenticationPackage(
1413 LocalFree(pTicketRequest);
1415 if (FAILED(Status) || FAILED(SubStatus))
1418 /* otherwise return ticket */
1419 *ticket = &(pTicketResponse->Ticket);
1422 /* set the initial flag if we were attempting to retrieve one
1423 * because Windows won't necessarily return the initial ticket
1426 if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_initial )
1427 (*ticket)->TicketFlags |= KERB_TICKET_FLAGS_initial;
1432 static krb5_error_code KRB5_CALLCONV krb5_lcc_close
1433 (krb5_context, krb5_ccache id);
1435 static krb5_error_code KRB5_CALLCONV krb5_lcc_destroy
1436 (krb5_context, krb5_ccache id);
1438 static krb5_error_code KRB5_CALLCONV krb5_lcc_end_seq_get
1439 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
1441 static krb5_error_code KRB5_CALLCONV krb5_lcc_generate_new
1442 (krb5_context, krb5_ccache *id);
1444 static const char * KRB5_CALLCONV krb5_lcc_get_name
1445 (krb5_context, krb5_ccache id);
1447 static krb5_error_code KRB5_CALLCONV krb5_lcc_get_principal
1448 (krb5_context, krb5_ccache id, krb5_principal *princ);
1450 static krb5_error_code KRB5_CALLCONV krb5_lcc_initialize
1451 (krb5_context, krb5_ccache id, krb5_principal princ);
1453 static krb5_error_code KRB5_CALLCONV krb5_lcc_next_cred
1454 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor,
1457 static krb5_error_code KRB5_CALLCONV krb5_lcc_resolve
1458 (krb5_context, krb5_ccache *id, const char *residual);
1460 static krb5_error_code KRB5_CALLCONV krb5_lcc_retrieve
1461 (krb5_context, krb5_ccache id, krb5_flags whichfields,
1462 krb5_creds *mcreds, krb5_creds *creds);
1464 static krb5_error_code KRB5_CALLCONV krb5_lcc_start_seq_get
1465 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
1467 static krb5_error_code KRB5_CALLCONV krb5_lcc_store
1468 (krb5_context, krb5_ccache id, krb5_creds *creds);
1470 static krb5_error_code KRB5_CALLCONV krb5_lcc_set_flags
1471 (krb5_context, krb5_ccache id, krb5_flags flags);
1473 static krb5_error_code KRB5_CALLCONV krb5_lcc_get_flags
1474 (krb5_context, krb5_ccache id, krb5_flags *flags);
1476 extern const krb5_cc_ops krb5_lcc_ops;
1478 krb5_error_code krb5_change_cache (void);
1481 krb5int_cc_creds_match_request(krb5_context, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds);
1485 typedef struct _krb5_lcc_data {
1489 krb5_principal princ;
1493 typedef struct _krb5_lcc_cursor {
1495 PKERB_QUERY_TKT_CACHE_RESPONSE w2k;
1496 PKERB_QUERY_TKT_CACHE_EX_RESPONSE xp;
1497 PKERB_QUERY_TKT_CACHE_EX2_RESPONSE ex2;
1500 PKERB_EXTERNAL_TICKET mstgt;
1506 * residual is ignored
1512 * Acccess the MS Kerberos LSA cache in the current logon session
1513 * Ignore the residual.
1516 * A filled in krb5_ccache structure "id".
1519 * KRB5_CC_NOMEM - there was insufficient memory to allocate the
1521 * krb5_ccache. id is undefined.
1524 static krb5_error_code KRB5_CALLCONV
1525 krb5_lcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
1528 krb5_lcc_data *data;
1531 PKERB_QUERY_TKT_CACHE_EX_RESPONSE pResponse;
1533 if (!PackageConnectLookup(&LogonHandle, &PackageId))
1534 return KRB5_FCC_NOFILE;
1536 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
1538 LsaDeregisterLogonProcess(LogonHandle);
1539 return KRB5_CC_NOMEM;
1542 lid->ops = &krb5_lcc_ops;
1544 lid->data = (krb5_pointer) malloc(sizeof(krb5_lcc_data));
1545 if (lid->data == NULL) {
1547 LsaDeregisterLogonProcess(LogonHandle);
1548 return KRB5_CC_NOMEM;
1551 lid->magic = KV5M_CCACHE;
1552 data = (krb5_lcc_data *)lid->data;
1553 data->LogonHandle = LogonHandle;
1554 data->PackageId = PackageId;
1557 data->cc_name = (char *)malloc(strlen(residual)+1);
1558 if (data->cc_name == NULL) {
1561 LsaDeregisterLogonProcess(LogonHandle);
1562 return KRB5_CC_NOMEM;
1564 strcpy(data->cc_name, residual);
1566 /* If there are already tickets present, grab a client principal name. */
1567 if (GetQueryTktCacheResponseEx(LogonHandle, PackageId, &pResponse)) {
1568 /* Take the first client principal we find; they should all be the
1570 for (i = 0; i < pResponse->CountOfTickets; i++) {
1571 if (UnicodeStringToMITPrinc(&pResponse->Tickets[i].ClientName,
1572 &pResponse->Tickets[i].ClientRealm,
1573 context, &data->princ))
1577 LsaFreeReturnBuffer(pResponse);
1581 * other routines will get errors on open, and callers must expect them,
1582 * if cache is non-existent/unusable
1589 * return success although we do not do anything
1590 * We should delete all tickets belonging to the specified principal
1593 static krb5_error_code KRB5_CALLCONV
1594 krb5_lcc_remove_cred(krb5_context context, krb5_ccache id, krb5_flags flags,
1597 static krb5_error_code KRB5_CALLCONV
1598 krb5_lcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
1600 krb5_cc_cursor cursor;
1601 krb5_error_code code;
1604 code = krb5_cc_start_seq_get(context, id, &cursor);
1606 if (code == KRB5_CC_NOTFOUND)
1611 while ( !(code = krb5_cc_next_cred(context, id, &cursor, &cred)) )
1613 if ( krb5_principal_compare(context, princ, cred.client) ) {
1614 code = krb5_lcc_remove_cred(context, id, 0, &cred);
1616 krb5_free_cred_contents(context, &cred);
1619 if (code == KRB5_CC_END || code == KRB5_CC_NOTFOUND)
1621 krb5_cc_end_seq_get(context, id, &cursor);
1632 * Closes the microsoft lsa cache, invalidates the id, and frees any resources
1633 * associated with the cache.
1635 static krb5_error_code KRB5_CALLCONV
1636 krb5_lcc_close(krb5_context context, krb5_ccache id)
1638 register int closeval = KRB5_OK;
1639 register krb5_lcc_data *data;
1642 data = (krb5_lcc_data *) id->data;
1645 LsaDeregisterLogonProcess(data->LogonHandle);
1647 free(data->cc_name);
1657 * Destroys the contents of id.
1662 static krb5_error_code KRB5_CALLCONV
1663 krb5_lcc_destroy(krb5_context context, krb5_ccache id)
1665 register krb5_lcc_data *data;
1668 data = (krb5_lcc_data *) id->data;
1670 return PurgeAllTickets(data->LogonHandle, data->PackageId) ? KRB5_OK : KRB5_FCC_INTERNAL;
1672 return KRB5_FCC_INTERNAL;
1677 * Prepares for a sequential search of the credentials cache.
1678 * Returns a krb5_cc_cursor to be used with krb5_lcc_next_cred and
1679 * krb5_lcc_end_seq_get.
1681 * If the cache is modified between the time of this call and the time
1682 * of the final krb5_lcc_end_seq_get, the results are undefined.
1686 * KRB5_FCC_INTERNAL - system errors
1688 static krb5_error_code KRB5_CALLCONV
1689 krb5_lcc_start_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1691 krb5_lcc_cursor *lcursor;
1692 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
1694 lcursor = (krb5_lcc_cursor *) malloc(sizeof(krb5_lcc_cursor));
1695 if (lcursor == NULL) {
1697 return KRB5_CC_NOMEM;
1701 * obtain a tgt to refresh the ccache in case the ticket is expired
1703 if (!GetMSTGT(context, data->LogonHandle, data->PackageId, &lcursor->mstgt, TRUE)) {
1706 return KRB5_CC_NOTFOUND;
1709 if ( does_query_ticket_cache_ex2() ) {
1710 if (!GetQueryTktCacheResponseEx2(data->LogonHandle, data->PackageId,
1711 &lcursor->response.ex2)) {
1712 LsaFreeReturnBuffer(lcursor->mstgt);
1715 return KRB5_FCC_INTERNAL;
1718 if (!GetQueryTktCacheResponseEx(data->LogonHandle, data->PackageId,
1719 &lcursor->response.xp)) {
1720 LsaFreeReturnBuffer(lcursor->mstgt);
1723 return KRB5_FCC_INTERNAL;
1726 *cursor = (krb5_cc_cursor) lcursor;
1733 * cursor is a krb5_cc_cursor originally obtained from
1734 * krb5_lcc_start_seq_get.
1740 * Fills in creds with the TGT obtained from the MS LSA
1742 * The cursor is updated to indicate TGT retrieval
1746 * KRB5_FCC_INTERNAL - system errors
1748 static krb5_error_code KRB5_CALLCONV
1749 krb5_lcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor, krb5_creds *creds)
1751 krb5_lcc_cursor *lcursor = (krb5_lcc_cursor *) *cursor;
1752 krb5_lcc_data *data;
1753 KERB_EXTERNAL_TICKET *msticket;
1754 krb5_error_code retval = KRB5_OK;
1756 data = (krb5_lcc_data *)id->data;
1759 if ( does_query_ticket_cache_ex2() ) {
1760 if ( lcursor->index >= lcursor->response.ex2->CountOfTickets ) {
1761 if (retval == KRB5_OK)
1764 LsaFreeReturnBuffer(lcursor->mstgt);
1765 LsaFreeReturnBuffer(lcursor->response.ex2);
1772 if ( data->flags & KRB5_TC_NOTICKET ) {
1773 if (!CacheInfoEx2ToMITCred( &lcursor->response.ex2->Tickets[lcursor->index++],
1775 retval = KRB5_FCC_INTERNAL;
1780 if (!GetMSCacheTicketFromCacheInfoEx2(data->LogonHandle,
1782 &lcursor->response.ex2->Tickets[lcursor->index++],&msticket)) {
1783 retval = KRB5_FCC_INTERNAL;
1788 if (lcursor->index >= lcursor->response.xp->CountOfTickets) {
1789 if (retval == KRB5_OK) {
1792 LsaFreeReturnBuffer(lcursor->mstgt);
1793 LsaFreeReturnBuffer(lcursor->response.xp);
1800 if (!GetMSCacheTicketFromCacheInfoEx(data->LogonHandle,
1802 &lcursor->response.xp->Tickets[lcursor->index++],
1804 retval = KRB5_FCC_INTERNAL;
1809 /* Don't return tickets with NULL Session Keys */
1810 if ( IsMSSessionKeyNull(&msticket->SessionKey) ) {
1811 LsaFreeReturnBuffer(msticket);
1815 /* convert the ticket */
1816 if ( does_query_ticket_cache_ex2() ) {
1817 if (!MSCredToMITCred(msticket, lcursor->response.ex2->Tickets[lcursor->index-1].ClientRealm, context, creds))
1818 retval = KRB5_FCC_INTERNAL;
1820 if (!MSCredToMITCred(msticket,
1821 lcursor->response.xp->Tickets[lcursor->index -
1824 retval = KRB5_FCC_INTERNAL;
1826 LsaFreeReturnBuffer(msticket);
1832 * cursor is a krb5_cc_cursor originally obtained from
1833 * krb5_lcc_start_seq_get.
1839 * Finishes sequential processing of the file credentials ccache id,
1840 * and invalidates the cursor (it must never be used after this call).
1843 static krb5_error_code KRB5_CALLCONV
1844 krb5_lcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
1846 krb5_lcc_cursor *lcursor = (krb5_lcc_cursor *) *cursor;
1849 LsaFreeReturnBuffer(lcursor->mstgt);
1850 if ( does_query_ticket_cache_ex2() )
1851 LsaFreeReturnBuffer(lcursor->response.ex2);
1853 LsaFreeReturnBuffer(lcursor->response.xp);
1864 * KRB5_CC_READONLY - not supported
1866 static krb5_error_code KRB5_CALLCONV
1867 krb5_lcc_generate_new (krb5_context context, krb5_ccache *id)
1869 return KRB5_CC_READONLY;
1874 * id is a ms lsa credential cache
1877 * The ccname specified during the krb5_lcc_resolve call
1879 static const char * KRB5_CALLCONV
1880 krb5_lcc_get_name (krb5_context context, krb5_ccache id)
1886 return (char *) ((krb5_lcc_data *) id->data)->cc_name;
1894 * Retrieves the primary principal from id, as set with
1895 * krb5_lcc_initialize. The principal is returned is allocated
1896 * storage that must be freed by the caller via krb5_free_principal.
1902 static krb5_error_code KRB5_CALLCONV
1903 krb5_lcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
1905 PKERB_QUERY_TKT_CACHE_EX_RESPONSE pResponse;
1906 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
1909 /* obtain principal */
1911 return krb5_copy_principal(context, data->princ, princ);
1913 if (GetQueryTktCacheResponseEx(data->LogonHandle, data->PackageId,
1915 /* Take the first client principal we find; they should all be the
1917 for (i = 0; i < pResponse->CountOfTickets; i++) {
1918 if (UnicodeStringToMITPrinc(&pResponse->Tickets[i].ClientName,
1919 &pResponse->Tickets[i].ClientRealm,
1920 context, &data->princ))
1923 LsaFreeReturnBuffer(pResponse);
1925 return krb5_copy_principal(context, data->princ, princ);
1928 return KRB5_CC_NOTFOUND;
1932 static krb5_error_code KRB5_CALLCONV
1933 krb5_lcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields,
1934 krb5_creds *mcreds, krb5_creds *creds)
1936 krb5_error_code kret = KRB5_OK;
1937 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
1938 KERB_EXTERNAL_TICKET *msticket = 0, *mstgt = 0, *mstmp = 0;
1939 krb5_creds * mcreds_noflags = 0;
1940 krb5_creds fetchcreds;
1941 PKERB_QUERY_TKT_CACHE_EX_RESPONSE pResponse = 0;
1944 memset(&fetchcreds, 0, sizeof(krb5_creds));
1946 /* first try to find out if we have an existing ticket which meets the requirements */
1947 kret = k5_cc_retrieve_cred_default(context, id, whichfields, mcreds,
1949 /* This sometimes returns a zero-length ticket; work around it. */
1950 if ( !kret && creds->ticket.length > 0 )
1953 /* if not, we must try to get a ticket without specifying any flags or etypes */
1954 kret = krb5_copy_creds(context, mcreds, &mcreds_noflags);
1957 mcreds_noflags->ticket_flags = 0;
1958 mcreds_noflags->keyblock.enctype = 0;
1960 if (!GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, mcreds_noflags, &msticket)) {
1961 kret = KRB5_CC_NOTFOUND;
1965 /* try again to find out if we have an existing ticket which meets the requirements */
1966 kret = k5_cc_retrieve_cred_default(context, id, whichfields, mcreds,
1968 /* This sometimes returns a zero-length ticket; work around it. */
1969 if ( !kret && creds->ticket.length > 0 )
1972 /* if not, obtain a ticket using the request flags and enctype even though it may not
1973 * be stored in the LSA cache for future use.
1976 LsaFreeReturnBuffer(msticket);
1980 if (!GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, mcreds, &msticket)) {
1981 kret = KRB5_CC_NOTFOUND;
1985 /* convert the ticket */
1987 * We can obtain the correct client realm for a ticket by walking the
1988 * cache contents until we find the matching service ticket.
1991 if (!GetQueryTktCacheResponseEx(data->LogonHandle, data->PackageId,
1993 kret = KRB5_FCC_INTERNAL;
1997 for (i = 0; i < pResponse->CountOfTickets; i++) {
1998 if (!GetMSCacheTicketFromCacheInfoEx(data->LogonHandle,
2000 &pResponse->Tickets[i], &mstmp)) {
2004 if (KerbExternalTicketMatch(msticket,mstmp))
2007 LsaFreeReturnBuffer(mstmp);
2011 if (!MSCredToMITCred(msticket, mstmp ?
2012 pResponse->Tickets[i].ClientRealm :
2013 msticket->DomainName, context, &fetchcreds)) {
2014 LsaFreeReturnBuffer(pResponse);
2015 kret = KRB5_FCC_INTERNAL;
2018 LsaFreeReturnBuffer(pResponse);
2021 /* check to see if this ticket matches the request using logic from
2022 * k5_cc_retrieve_cred_default()
2024 if ( krb5int_cc_creds_match_request(context, whichfields, mcreds, &fetchcreds) ) {
2025 *creds = fetchcreds;
2027 krb5_free_cred_contents(context, &fetchcreds);
2028 kret = KRB5_CC_NOTFOUND;
2033 LsaFreeReturnBuffer(mstmp);
2035 LsaFreeReturnBuffer(mstgt);
2037 LsaFreeReturnBuffer(msticket);
2038 if ( mcreds_noflags )
2039 krb5_free_creds(context, mcreds_noflags);
2045 * We can't write to the MS LSA cache. So we request the cache to obtain a ticket for the same
2046 * principal in the hope that next time the application requires a ticket for the service it
2047 * is attempt to store, the retrieved ticket will be good enough.
2050 * KRB5_CC_READONLY - not supported
2052 static krb5_error_code KRB5_CALLCONV
2053 krb5_lcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
2055 krb5_error_code kret = KRB5_OK;
2056 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
2057 KERB_EXTERNAL_TICKET *msticket = 0, *msticket2 = 0;
2058 krb5_creds * creds_noflags = 0;
2060 if (krb5_is_config_principal(context, creds->server)) {
2061 /* mslsa cannot store config creds, so we have to bail.
2062 * The 'right' thing to do would be to return an appropriate error,
2063 * but that would require modifying the calling code to check
2064 * for that error and ignore it.
2069 if (KerbSubmitTicket( data->LogonHandle, data->PackageId, context, creds ))
2072 /* If not, lets try to obtain a matching ticket from the KDC */
2073 if ( creds->ticket_flags != 0 && creds->keyblock.enctype != 0 ) {
2074 /* if not, we must try to get a ticket without specifying any flags or etypes */
2075 kret = krb5_copy_creds(context, creds, &creds_noflags);
2077 creds_noflags->ticket_flags = 0;
2078 creds_noflags->keyblock.enctype = 0;
2080 GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, creds_noflags, &msticket2);
2081 krb5_free_creds(context, creds_noflags);
2085 GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, creds, &msticket);
2086 if (msticket || msticket2) {
2088 LsaFreeReturnBuffer(msticket);
2090 LsaFreeReturnBuffer(msticket2);
2093 return KRB5_CC_READONLY;
2097 * Individual credentials can be implemented differently depending
2098 * on the operating system version. (undocumented.)
2103 static krb5_error_code KRB5_CALLCONV
2104 krb5_lcc_remove_cred(krb5_context context, krb5_ccache id, krb5_flags flags,
2107 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
2109 if (PurgeTicketEx(data->LogonHandle, data->PackageId, context, flags,
2113 return KRB5_CC_READONLY;
2121 static krb5_error_code KRB5_CALLCONV
2122 krb5_lcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
2124 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
2126 data->flags = flags;
2130 static krb5_error_code KRB5_CALLCONV
2131 krb5_lcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
2133 krb5_lcc_data *data = (krb5_lcc_data *)id->data;
2135 *flags = data->flags;
2139 static krb5_error_code KRB5_CALLCONV
2140 krb5_lcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor)
2142 krb5_cc_ptcursor new_cursor = (krb5_cc_ptcursor )malloc(sizeof(*new_cursor));
2145 new_cursor->ops = &krb5_lcc_ops;
2146 new_cursor->data = (krb5_pointer)(1);
2147 *cursor = new_cursor;
2152 static krb5_error_code KRB5_CALLCONV
2153 krb5_lcc_ptcursor_next(krb5_context context, krb5_cc_ptcursor cursor, krb5_ccache *ccache)
2155 krb5_error_code code = 0;
2157 if (cursor->data == NULL)
2160 cursor->data = NULL;
2161 if ((code = krb5_lcc_resolve(context, ccache, ""))) {
2162 if (code != KRB5_FCC_NOFILE)
2163 /* Note that we only want to return serious errors.
2164 * Any non-zero return code will prevent the cccol iterator
2165 * from advancing to the next ccache collection. */
2171 static krb5_error_code KRB5_CALLCONV
2172 krb5_lcc_ptcursor_free(krb5_context context, krb5_cc_ptcursor *cursor)
2181 const krb5_cc_ops krb5_lcc_ops = {
2186 krb5_lcc_generate_new,
2187 krb5_lcc_initialize,
2192 krb5_lcc_get_principal,
2193 krb5_lcc_start_seq_get,
2195 krb5_lcc_end_seq_get,
2196 krb5_lcc_remove_cred,
2199 krb5_lcc_ptcursor_new,
2200 krb5_lcc_ptcursor_next,
2201 krb5_lcc_ptcursor_free,
2203 NULL, /* lastchange */
2204 NULL, /* wasdefault */
2207 NULL, /* switch_to */