From fc89820304dc65798949610f898ddc747c7222c3 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Fri, 19 Dec 2003 00:19:20 +0000 Subject: [PATCH] * cc_retr.c: Extract the test to determine if a credential matches a requested credential according to the specified fields into a private function: krb5int_cc_creds_match_request() * cc_mslsa.c: Extend the functionality of krb5_lcc_retrieve() to perform a MS Kerberos LSA ticket request if there is no matching credential in the cache. The MS Kerberos LSA places the following restriction on what tickets it will place into the LSA cache: tickets obtained by an application request for a specific set of kerberos flags or enctype will not be cached. Therefore, we first make a request with no flags or enctype in the hope that we will be lucky and get the right ones anyway. If not, we make the application's request and return that ticket if it matches the other criteria. Implemented a similar technique for krb5_lcc_store(). Since we can not write to the cache, when a store request is made we instead perform a ticket request through the lsa for a matching credential. If we receive one, we return success. Otherwise, we return the KRB5_CC_READONLY error. With these changes I am now able to operate entirely with the MSLSA ccache as the default cache provided the MS LSA credentials are for the principal I wish to use. Obviously, one cannot change principals while the MSLSA ccache is the default. ticket: 2049 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@15939 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/ccache/ChangeLog | 28 ++++++ src/lib/krb5/ccache/cc_mslsa.c | 162 +++++++++++++++++++++++++++++++-- src/lib/krb5/ccache/cc_retr.c | 64 +++++++------ 3 files changed, 217 insertions(+), 37 deletions(-) diff --git a/src/lib/krb5/ccache/ChangeLog b/src/lib/krb5/ccache/ChangeLog index edfed8589..5eab3ca8d 100644 --- a/src/lib/krb5/ccache/ChangeLog +++ b/src/lib/krb5/ccache/ChangeLog @@ -1,3 +1,31 @@ +2003-12-18 Jeffrey Altman + + * cc_retr.c: Extract the test to determine if a credential matches + a requested credential according to the specified fields into + a private function: krb5int_cc_creds_match_request() + + * cc_mslsa.c: Extend the functionality of krb5_lcc_retrieve() to + perform a MS Kerberos LSA ticket request if there is no matching + credential in the cache. The MS Kerberos LSA places the following + restriction on what tickets it will place into the LSA cache: + tickets obtained by an application request for a specific + set of kerberos flags or enctype will not be cached. + Therefore, we first make a request with no flags or enctype in + the hope that we will be lucky and get the right ones anyway. + If not, we make the application's request and return that ticket + if it matches the other criteria. + + Implemented a similar technique for krb5_lcc_store(). Since we + can not write to the cache, when a store request is made we + instead perform a ticket request through the lsa for a matching + credential. If we receive one, we return success. Otherwise, + we return the KRB5_CC_READONLY error. + + With these changes I am now able to operate entirely with the MSLSA + ccache as the default cache provided the MS LSA credentials are + for the principal I wish to use. Obviously, one cannot change + principals while the MSLSA ccache is the default. + 2003-12-15 Jeffrey Altman * cc_msla.c: Enable purging of the MS Kerberos LSA cache when the TGT diff --git a/src/lib/krb5/ccache/cc_mslsa.c b/src/lib/krb5/ccache/cc_mslsa.c index 039959a4e..464fd9527 100644 --- a/src/lib/krb5/ccache/cc_mslsa.c +++ b/src/lib/krb5/ccache/cc_mslsa.c @@ -63,10 +63,12 @@ #include #include +#define MAX_MSG_SIZE 256 +#define MAX_MSPRINC_SIZE 1024 + 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 @@ -150,6 +152,18 @@ ANSIToUnicode(LPSTR lpInputString, LPTSTR lpszOutputString, int nOutStringLen) } // ANSIToUnicode +static void +MITPrincToMSPrinc(krb5_context context, krb5_principal principal, UNICODE_STRING * msprinc) +{ + char *aname = NULL; + + if (!krb5_unparse_name(context, principal, &aname)) { + msprinc->Length = strlen(aname) * sizeof(WCHAR); + ANSIToUnicode(aname, msprinc->Buffer, msprinc->MaximumLength); + krb5_free_unparsed_name(context,aname); + } +} + static void MSPrincToMITPrinc(KERB_EXTERNAL_NAME *msprinc, WCHAR *realm, krb5_context context, krb5_principal *principal) { @@ -304,9 +318,11 @@ ConcatenateUnicodeStrings(UNICODE_STRING *pTarget, UNICODE_STRING Source1, UNICO if (TotalSize > pTarget->MaximumLength) return ERROR_INSUFFICIENT_BUFFER; - pTarget->Length = TotalSize; - memcpy(buffer, Source1.Buffer, Source1.Length); + if ( pTarget->Buffer != Source1.Buffer ) + memcpy(buffer, Source1.Buffer, Source1.Length); memcpy(buffer + Source1.Length, Source2.Buffer, Source2.Length); + + pTarget->Length = TotalSize; return ERROR_SUCCESS; } @@ -828,8 +844,59 @@ FreeQueryResponse(PKERB_QUERY_TKT_CACHE_RESPONSE pResponse) LsaFreeReturnBuffer(pResponse); } + +static BOOL +GetMSCacheTicketFromMITCred( HANDLE LogonHandle, ULONG PackageId, + krb5_context context, krb5_creds *creds, 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) + MAX_MSPRINC_SIZE; + + 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 = 0; + pTicketRequest->TargetName.MaximumLength = MAX_MSPRINC_SIZE; + pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1); + MITPrincToMSPrinc(context, creds->server, &pTicketRequest->TargetName); + pTicketRequest->CacheOptions = 0; + pTicketRequest->TicketFlags = creds->ticket_flags; + pTicketRequest->EncryptionType = creds->keyblock.enctype; + + Status = LsaCallAuthenticationPackage( + LogonHandle, + PackageId, + pTicketRequest, + RequestSize, + &pTicketResponse, + &ResponseSize, + &SubStatus + ); + + LocalFree(pTicketRequest); + + if (FAILED(Status) || FAILED(SubStatus)) + return(FALSE); + + /* otherwise return ticket */ + *ticket = &(pTicketResponse->Ticket); + return(TRUE); + +} + static BOOL -GetMSCacheTicket( HANDLE LogonHandle, ULONG PackageId, +GetMSCacheTicketFromCacheInfo( HANDLE LogonHandle, ULONG PackageId, PKERB_TICKET_CACHE_INFO tktinfo, PKERB_EXTERNAL_TICKET *ticket) { NTSTATUS Status = 0; @@ -866,7 +933,7 @@ GetMSCacheTicket( HANDLE LogonHandle, ULONG PackageId, &SubStatus ); - LsaFreeReturnBuffer(pTicketRequest); + LocalFree(pTicketRequest); if (FAILED(Status) || FAILED(SubStatus)) return(FALSE); @@ -922,6 +989,9 @@ extern const krb5_cc_ops krb5_lcc_ops; krb5_error_code krb5_change_cache (void); +krb5_boolean +krb5int_cc_creds_match_request(krb5_context, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds); + #define KRB5_OK 0 typedef struct _krb5_lcc_data { @@ -1140,7 +1210,7 @@ krb5_lcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor, if ( lcursor->index >= lcursor->response->CountOfTickets ) return KRB5_CC_END; - if (!GetMSCacheTicket(data->LogonHandle, data->PackageId, + if (!GetMSCacheTicketFromCacheInfo(data->LogonHandle, data->PackageId, &lcursor->response->Tickets[lcursor->index++],&msticket)) return KRB5_FCC_INTERNAL; @@ -1209,7 +1279,7 @@ krb5_lcc_get_name (krb5_context context, krb5_ccache id) * * Errors: * system errors - * KRB5_CC_NOMEM + * KRB5_CC_NOT_KTYPE */ static krb5_error_code KRB5_CALLCONV krb5_lcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ) @@ -1225,18 +1295,92 @@ 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); + krb5_error_code kret = KRB5_OK; + krb5_lcc_data *data = (krb5_lcc_data *)id->data; + KERB_EXTERNAL_TICKET *msticket = 0; + krb5_creds * mcreds_noflags; + krb5_creds * fetchcreds; + + /* first try to find out if we have an existing ticket which meets the requirements */ + kret = krb5_cc_retrieve_cred_default (context, id, whichfields, mcreds, creds); + if ( !kret ) + return KRB5_OK; + + /* if not, we must try to get a ticket without specifying any flags or etypes */ + krb5_copy_creds(context, mcreds, &mcreds_noflags); + mcreds_noflags->ticket_flags = 0; + mcreds_noflags->keyblock.enctype = 0; + + if (!GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, mcreds_noflags, &msticket)) { + kret = KRB5_CC_NOTFOUND; + goto cleanup; + } + + /* try again to find out if we have an existing ticket which meets the requirements */ + kret = krb5_cc_retrieve_cred_default (context, id, whichfields, mcreds, creds); + if ( !kret ) + goto cleanup; + + /* if not, obtain a ticket using the request flags and enctype even though it will not + * be stored in the LSA cache for future use. + */ + if ( msticket ) { + LsaFreeReturnBuffer(msticket); + msticket = 0; + } + + if (!GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, mcreds, &msticket)) { + kret = KRB5_CC_NOTFOUND; + goto cleanup; + } + + /* convert the ticket */ + MSCredToMITCred(msticket, context, fetchcreds); + + /* check to see if this ticket matches the request using logic from + * krb5_cc_retrieve_cred_default() + */ + if ( krb5int_cc_creds_match_request(context, whichfields, mcreds, fetchcreds) ) { + creds = fetchcreds; + } else { + krb5_free_creds(context, fetchcreds); + kret = KRB5_CC_NOTFOUND; + } + + cleanup: + if ( msticket ) + LsaFreeReturnBuffer(msticket); + if ( mcreds_noflags ) + krb5_free_creds(context, mcreds_noflags); + return kret; } /* + * We can't write to the MS LSA cache. So we request the cache to obtain a ticket for the same + * principal in the hope that next time the application requires a ticket for the service it + * is attempt to store, the retrieved ticket will be good enough. + * * Errors: * KRB5_CC_READONLY - not supported */ static krb5_error_code KRB5_CALLCONV krb5_lcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds) { + krb5_error_code kret = KRB5_OK; + krb5_lcc_data *data = (krb5_lcc_data *)id->data; + KERB_EXTERNAL_TICKET *msticket = 0; + krb5_creds * creds_noflags; + + /* if not, we must try to get a ticket without specifying any flags or etypes */ + krb5_copy_creds(context, creds, &creds_noflags); + creds_noflags->ticket_flags = 0; + creds_noflags->keyblock.enctype = 0; + + if (GetMSCacheTicketFromMITCred(data->LogonHandle, data->PackageId, context, creds_noflags, &msticket)) { + LsaFreeReturnBuffer(msticket); + return KRB5_OK; + } return KRB5_CC_READONLY; } diff --git a/src/lib/krb5/ccache/cc_retr.c b/src/lib/krb5/ccache/cc_retr.c index ebd6193cd..80d31640c 100644 --- a/src/lib/krb5/ccache/cc_retr.c +++ b/src/lib/krb5/ccache/cc_retr.c @@ -157,6 +157,40 @@ pref (krb5_enctype my_ktype, int nktypes, krb5_enctype *ktypes) * KRB5_CC_NOT_KTYPE */ +krb5_boolean +krb5int_cc_creds_match_request(krb5_context context, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds) +{ + if (((set(KRB5_TC_MATCH_SRV_NAMEONLY) && + srvname_match(context, mcreds, creds)) || + standard_fields_match(context, mcreds, creds)) + && + (! set(KRB5_TC_MATCH_IS_SKEY) || + mcreds->is_skey == creds->is_skey) + && + (! set(KRB5_TC_MATCH_FLAGS_EXACT) || + mcreds->ticket_flags == creds->ticket_flags) + && + (! set(KRB5_TC_MATCH_FLAGS) || + flags_match(mcreds->ticket_flags, creds->ticket_flags)) + && + (! set(KRB5_TC_MATCH_TIMES_EXACT) || + times_match_exact(&mcreds->times, &creds->times)) + && + (! set(KRB5_TC_MATCH_TIMES) || + times_match(&mcreds->times, &creds->times)) + && + ( ! set(KRB5_TC_MATCH_AUTHDATA) || + authdata_match(mcreds->authdata, creds->authdata)) + && + (! set(KRB5_TC_MATCH_2ND_TKT) || + data_match (&mcreds->second_ticket, &creds->second_ticket)) + && + ((! set(KRB5_TC_MATCH_KTYPE))|| + (mcreds->keyblock.enctype == creds->keyblock.enctype))) + return TRUE; + return FALSE; +} + static krb5_error_code krb5_cc_retrieve_cred_seq (krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds, int nktypes, krb5_enctype *ktypes) { @@ -178,34 +212,8 @@ krb5_cc_retrieve_cred_seq (krb5_context context, krb5_ccache id, krb5_flags whic return kret; while ((kret = krb5_cc_next_cred(context, id, &cursor, &fetchcreds)) == KRB5_OK) { - if (((set(KRB5_TC_MATCH_SRV_NAMEONLY) && - srvname_match(context, mcreds, &fetchcreds)) || - standard_fields_match(context, mcreds, &fetchcreds)) - && - (! set(KRB5_TC_MATCH_IS_SKEY) || - mcreds->is_skey == fetchcreds.is_skey) - && - (! set(KRB5_TC_MATCH_FLAGS_EXACT) || - mcreds->ticket_flags == fetchcreds.ticket_flags) - && - (! set(KRB5_TC_MATCH_FLAGS) || - flags_match(mcreds->ticket_flags, fetchcreds.ticket_flags)) - && - (! set(KRB5_TC_MATCH_TIMES_EXACT) || - times_match_exact(&mcreds->times, &fetchcreds.times)) - && - (! set(KRB5_TC_MATCH_TIMES) || - times_match(&mcreds->times, &fetchcreds.times)) - && - ( ! set(KRB5_TC_MATCH_AUTHDATA) || - authdata_match(mcreds->authdata, fetchcreds.authdata)) - && - (! set(KRB5_TC_MATCH_2ND_TKT) || - data_match (&mcreds->second_ticket, &fetchcreds.second_ticket)) - && - ((! set(KRB5_TC_MATCH_KTYPE))|| - (mcreds->keyblock.enctype == fetchcreds.keyblock.enctype))) - { + if (krb5int_cc_creds_match_request(context, whichfields, mcreds, &fetchcreds)) + { if (ktypes) { fetched.pref = pref (fetchcreds.keyblock.enctype, nktypes, ktypes); -- 2.26.2