* cc_retr.c: Extract the test to determine if a credential matches
authorJeffrey Altman <jaltman@secure-endpoints.com>
Fri, 19 Dec 2003 00:19:20 +0000 (00:19 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Fri, 19 Dec 2003 00:19:20 +0000 (00:19 +0000)
     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
src/lib/krb5/ccache/cc_mslsa.c
src/lib/krb5/ccache/cc_retr.c

index edfed858927023ee3b0949304b299374b570a977..5eab3ca8ddbd96388cbe1956d0d48b1b665b278e 100644 (file)
@@ -1,3 +1,31 @@
+2003-12-18  Jeffrey Altman <jaltman@mit.edu>
+
+   * 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 <jaltman@mit.edu>
 
    * cc_msla.c:   Enable purging of the MS Kerberos LSA cache when the TGT
index 039959a4ef2fc5582ae48106dcb616a492cd01aa..464fd9527c80ada8c46418ce5a99e317f12318c7 100644 (file)
 #include <security.h>
 #include <ntsecapi.h>
 
+#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;
 }
 
index ebd6193cd6df8cd46f7cc2321dd97317a1488a81..80d31640c08e9c24ab41b4aa76e3f63b963f86f5 100644 (file)
@@ -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);