* cc_mslsa.c:
authorJeffrey Altman <jaltman@secure-endpoints.com>
Thu, 2 Sep 2004 02:31:52 +0000 (02:31 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Thu, 2 Sep 2004 02:31:52 +0000 (02:31 +0000)
        - Fix MITPrincToMSPrinc to prevent writing to the output
          buffer if the input won't fit.
        - Add internal UnicodeStringToMITPrinc function
        - Rename internal MSPrincToMITPrinc to ExternalNameToMITPrinc
        - Rename internal PurgeMSTGT to PurgeAllTickets
        - Add internal PurgeTicket2000
        - Add internal PurgeTicketXP
        - Since tickets can only be requested via KDC Opt Flags it is
          not possible to specifically request the Initial ticket.  If
          more than one ticket exists which matching service names,
          enctypes, and ticket flags the initial ticket flag may not be
          set.  If the caller requested the initial ticket, set the flag
          manually.
        - Add preliminary support for krb5_lcc_set_flags
        - Modify krb5_lcc_initialize to return success
        - Modify krb5_lcc_get_principal to support an LSA cache
          which does not contain a TGT when krb5_lcc_resolve is
          called.
        - Implement krb5_lcc_remove_cred

ticket: new

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16714 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/krb5/ccache/ChangeLog
src/lib/krb5/ccache/cc_mslsa.c

index dbf0a8b5b700efb3736f303b25ef17a9a4221ba7..09ea4d9c6cdcc9422a06e8234e7499ed8753e46a 100644 (file)
@@ -1,3 +1,27 @@
+2004-09-01  Jeffrey Altman <jaltman@mit.edu>
+
+        * cc_mslsa.c: 
+        - Fix MITPrincToMSPrinc to prevent writing to the output
+          buffer if the input won't fit.
+        - Add internal UnicodeStringToMITPrinc function
+        - Rename internal MSPrincToMITPrinc to ExternalNameToMITPrinc
+        - Rename internal PurgeMSTGT to PurgeAllTickets
+        - Add internal PurgeTicket2000 
+        - Add internal PurgeTicketXP
+        - Since tickets can only be requested via KDC Opt Flags it is
+          not possible to specifically request the Initial ticket.  If
+          more than one ticket exists which matching service names,
+          enctypes, and ticket flags the initial ticket flag may not be
+          set.  If the caller requested the initial ticket, set the flag
+          manually.
+        - Add preliminary support for krb5_lcc_set_flags
+        - Modify krb5_lcc_initialize to return success
+        - Modify krb5_lcc_get_principal to support an LSA cache
+          which does not contain a TGT when krb5_lcc_resolve is 
+          called.
+        - Implement krb5_lcc_remove_cred
+            
+
 2004-08-27  Ken Raeburn  <raeburn@mit.edu>
 
        * t_cc.c (init_test_cred): Terminate argument list to
index a32359ac30ceba246581fc86730a586280dd87db..422426a7dc200d263dc389dc2ea73c4a967354c0 100644 (file)
@@ -234,13 +234,33 @@ MITPrincToMSPrinc(krb5_context context, krb5_principal principal, UNICODE_STRING
 
     if (!krb5_unparse_name(context, principal, &aname)) {
         msprinc->Length = strlen(aname) * sizeof(WCHAR);
-        ANSIToUnicode(aname, msprinc->Buffer, msprinc->MaximumLength);
+        if ( msprinc->Length <= msprinc->MaximumLength )
+            ANSIToUnicode(aname, msprinc->Buffer, msprinc->MaximumLength);
+        else 
+            msprinc->Length = 0;
         krb5_free_unparsed_name(context,aname);
     }
 }
 
 static void
-MSPrincToMITPrinc(KERB_EXTERNAL_NAME *msprinc, WCHAR *realm, krb5_context context, 
+UnicodeStringToMITPrinc(UNICODE_STRING *service, WCHAR *realm, krb5_context context, 
+                  krb5_principal *principal)
+{
+    WCHAR princbuf[512];
+    char aname[512];
+
+    princbuf[0]=0;
+    wcsncpy(princbuf, service->Buffer, service->Length/sizeof(WCHAR));
+    princbuf[service->Length/sizeof(WCHAR)]=0;
+    wcscat(princbuf, L"@");
+    wcscat(princbuf, realm);
+    UnicodeToANSI(princbuf, aname, sizeof(aname));
+    krb5_parse_name(context, aname, principal);
+}
+
+
+static void
+KerbExternalNameToMITPrinc(KERB_EXTERNAL_NAME *msprinc, WCHAR *realm, krb5_context context, 
                   krb5_principal *principal)
 {
     WCHAR princbuf[512],tmpbuf[128];
@@ -261,7 +281,6 @@ MSPrincToMITPrinc(KERB_EXTERNAL_NAME *msprinc, WCHAR *realm, krb5_context contex
     krb5_parse_name(context, aname, principal);
 }
 
-
 static time_t
 FileTimeToUnixTime(LARGE_INTEGER *ltime)
 {
@@ -367,14 +386,13 @@ MSCredToMITCred(KERB_EXTERNAL_TICKET *msticket, UNICODE_STRING ClientRealm,
     // construct Client Principal
     wcsncpy(wrealm, ClientRealm.Buffer, ClientRealm.Length/sizeof(WCHAR));
     wrealm[ClientRealm.Length/sizeof(WCHAR)]=0;
-    MSPrincToMITPrinc(msticket->ClientName, wrealm, context, &creds->client);
+    KerbExternalNameToMITPrinc(msticket->ClientName, wrealm, context, &creds->client);
 
     // construct Service Principal
     wcsncpy(wrealm, msticket->DomainName.Buffer,
             msticket->DomainName.Length/sizeof(WCHAR));
     wrealm[msticket->DomainName.Length/sizeof(WCHAR)]=0;
-    MSPrincToMITPrinc(msticket->ServiceName, wrealm, context, &creds->server);
-
+    KerbExternalNameToMITPrinc(msticket->ServiceName, wrealm, context, &creds->server);
     MSSessionKeyToMITKeyblock(&msticket->SessionKey, context, 
                               &creds->keyblock);
     MSFlagsToMITFlags(msticket->TicketFlags, &creds->ticket_flags);
@@ -663,7 +681,7 @@ ConstructTicketRequest(UNICODE_STRING DomainName, PKERB_RETRIEVE_TKT_REQUEST * o
 }
 
 static BOOL
-PurgeMSTGT(HANDLE LogonHandle, ULONG  PackageId)
+PurgeAllTickets(HANDLE LogonHandle, ULONG  PackageId)
 {
     NTSTATUS Status = 0;
     NTSTATUS SubStatus = 0;
@@ -691,6 +709,145 @@ PurgeMSTGT(HANDLE LogonHandle, ULONG  PackageId)
     return TRUE;
 }
 
+
+static BOOL
+PurgeTicket2000( HANDLE LogonHandle, ULONG  PackageId, 
+                 krb5_context context, krb5_creds *cred )
+{
+    NTSTATUS Status = 0;
+    NTSTATUS SubStatus = 0;
+    KERB_PURGE_TKT_CACHE_REQUEST * pPurgeRequest;
+    DWORD dwRequestLen = sizeof(KERB_PURGE_TKT_CACHE_REQUEST) + 1024;
+    char * sname, * srealm = NULL;
+
+    if (krb5_unparse_name(context, cred->server, &sname))
+        return FALSE;
+
+    pPurgeRequest = malloc(dwRequestLen);
+    if ( pPurgeRequest == NULL )
+        return FALSE;
+    memset(pPurgeRequest, 0, dwRequestLen);
+
+    srealm = strrchr(sname, '@');
+    *srealm = '\0';
+    srealm++;
+
+    pPurgeRequest->MessageType = KerbPurgeTicketCacheMessage;
+    pPurgeRequest->LogonId.LowPart = 0;
+    pPurgeRequest->LogonId.HighPart = 0;
+    pPurgeRequest->ServerName.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_REQUEST));
+    pPurgeRequest->ServerName.Length = strlen(sname);
+    pPurgeRequest->ServerName.MaximumLength = 256;
+    ANSIToUnicode(sname, pPurgeRequest->ServerName.Buffer,
+                  pPurgeRequest->ServerName.MaximumLength);
+    pPurgeRequest->RealmName.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_REQUEST)+512);
+    pPurgeRequest->RealmName.Length = strlen(srealm);
+    pPurgeRequest->RealmName.MaximumLength = 256;
+    ANSIToUnicode(srealm, pPurgeRequest->RealmName.Buffer,
+                  pPurgeRequest->RealmName.MaximumLength);
+
+    Status = LsaCallAuthenticationPackage( LogonHandle,
+                                           PackageId,
+                                           pPurgeRequest,
+                                           dwRequestLen,
+                                           NULL,
+                                           NULL,
+                                           &SubStatus
+                                           );
+    free(pPurgeRequest);
+    krb5_free_unparsed_name(context, sname);
+
+    if (FAILED(Status) || FAILED(SubStatus))
+        return FALSE;
+
+    return TRUE;
+}
+
+
+static BOOL
+PurgeTicketXP( HANDLE LogonHandle, ULONG  PackageId, 
+               krb5_context context, krb5_flags flags, krb5_creds *cred)
+{
+    NTSTATUS Status = 0;
+    NTSTATUS SubStatus = 0;
+    KERB_PURGE_TKT_CACHE_EX_REQUEST * pPurgeRequest;
+    DWORD dwRequestLen = sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 2048;
+    char * cname, * crealm = NULL;
+    char * sname, * srealm = NULL;
+
+    if (krb5_unparse_name(context, cred->client, &cname))
+        return FALSE;
+    
+    if (krb5_unparse_name(context, cred->server, &sname)) {
+        krb5_free_unparsed_name(context, cname);
+        return FALSE;
+    }
+
+    pPurgeRequest = malloc(dwRequestLen);
+    if ( pPurgeRequest == NULL )
+        return FALSE;
+    memset(pPurgeRequest, 0, dwRequestLen);
+
+    crealm = strrchr(cname, '@');
+    *crealm = '\0';
+    crealm++;
+
+    srealm = strrchr(sname, '@');
+    *srealm = '\0';
+    srealm++;
+
+    pPurgeRequest->MessageType = KerbPurgeTicketCacheExMessage;
+    pPurgeRequest->LogonId.LowPart = 0;
+    pPurgeRequest->LogonId.HighPart = 0;
+    pPurgeRequest->Flags = 0;
+    pPurgeRequest->TicketTemplate.ClientName.Buffer = (PWSTR)((CHAR *)pPurgeRequest + sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST));
+    pPurgeRequest->TicketTemplate.ClientName.Length = strlen(cname);
+    pPurgeRequest->TicketTemplate.ClientName.MaximumLength = 256;
+    ANSIToUnicode(cname, pPurgeRequest->TicketTemplate.ClientName.Buffer,
+                  pPurgeRequest->TicketTemplate.ClientName.MaximumLength);
+
+    pPurgeRequest->TicketTemplate.ClientRealm.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 512);
+    pPurgeRequest->TicketTemplate.ClientRealm.Length = strlen(crealm);
+    pPurgeRequest->TicketTemplate.ClientRealm.MaximumLength = 256;
+    ANSIToUnicode(crealm, pPurgeRequest->TicketTemplate.ClientRealm.Buffer,
+                  pPurgeRequest->TicketTemplate.ClientRealm.MaximumLength);
+
+    pPurgeRequest->TicketTemplate.ServerName.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 1024);
+    pPurgeRequest->TicketTemplate.ServerName.Length = strlen(sname);
+    pPurgeRequest->TicketTemplate.ServerName.MaximumLength = 256;
+    ANSIToUnicode(sname, pPurgeRequest->TicketTemplate.ServerName.Buffer,
+                  pPurgeRequest->TicketTemplate.ServerName.MaximumLength);
+
+    pPurgeRequest->TicketTemplate.ServerRealm.Buffer = (PWSTR)(((CHAR *)pPurgeRequest)+sizeof(KERB_PURGE_TKT_CACHE_EX_REQUEST) + 1536);
+    pPurgeRequest->TicketTemplate.ServerRealm.Length = strlen(srealm);
+    pPurgeRequest->TicketTemplate.ServerRealm.MaximumLength = 256;
+    ANSIToUnicode(srealm, pPurgeRequest->TicketTemplate.ServerRealm.Buffer,
+                  pPurgeRequest->TicketTemplate.ServerRealm.MaximumLength);
+
+    pPurgeRequest->TicketTemplate.StartTime;
+    pPurgeRequest->TicketTemplate.EndTime;
+    pPurgeRequest->TicketTemplate.RenewTime;
+    pPurgeRequest->TicketTemplate.EncryptionType = cred->keyblock.enctype;
+    pPurgeRequest->TicketTemplate.TicketFlags = flags;
+
+    Status = LsaCallAuthenticationPackage( LogonHandle,
+                                           PackageId,
+                                           pPurgeRequest,
+                                           dwRequestLen,
+                                           NULL,
+                                           NULL,
+                                           &SubStatus
+                                           );
+    free(pPurgeRequest);
+    krb5_free_unparsed_name(context,cname);
+    krb5_free_unparsed_name(context,sname);
+
+    if (FAILED(Status) || FAILED(SubStatus))
+        return FALSE;
+    return TRUE;
+}
+
+
 /* 
  * A simple function to determine if there is an exact match between two tickets
  * We rely on the fact that the external tickets contain the raw Kerberos ticket.
@@ -914,7 +1071,7 @@ GetMSTGT(krb5_context context, HANDLE LogonHandle, ULONG PackageId, KERB_EXTERNA
             // be requested.  It is not possible to purge just the TGT.  All
             // service tickets must be purged.
             //
-            PurgeMSTGT(LogonHandle, PackageId);
+            PurgeAllTickets(LogonHandle, PackageId);
         }
 #endif /* ENABLE_PURGING */
     }
@@ -1112,7 +1269,6 @@ GetQueryTktCacheResponseXP( HANDLE LogonHandle, ULONG PackageId,
     return FALSE;
 }
 
-
 static BOOL
 GetMSCacheTicketFromMITCred( HANDLE LogonHandle, ULONG PackageId,
                              krb5_context context, krb5_creds *creds, 
@@ -1219,6 +1375,13 @@ GetMSCacheTicketFromCacheInfoW2K( HANDLE LogonHandle, ULONG PackageId,
     
     /* otherwise return ticket */
     *ticket = &(pTicketResponse->Ticket);
+
+    /* set the initial flag if we were attempting to retrieve one
+     * because Windows won't necessarily return the initial ticket
+     * to us.
+     */
+    if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_initial )
+        (*ticket)->TicketFlags &= KERB_TICKET_FLAGS_initial;
     return(TRUE);
 
 }
@@ -1248,8 +1411,6 @@ GetMSCacheTicketFromCacheInfoXP( HANDLE LogonHandle, ULONG PackageId,
     pTicketRequest->TargetName.Buffer = (PWSTR) (pTicketRequest + 1);
     memcpy(pTicketRequest->TargetName.Buffer,tktinfo->ServerName.Buffer, tktinfo->ServerName.Length);
     pTicketRequest->CacheOptions = 0;
-    if ( does_retrieve_ticket_cache_ticket() )
-        pTicketRequest->CacheOptions |= KERB_RETRIEVE_TICKET_CACHE_TICKET;
     pTicketRequest->EncryptionType = tktinfo->EncryptionType;
     pTicketRequest->TicketFlags = 0;
     if ( tktinfo->TicketFlags & KERB_TICKET_FLAGS_forwardable )
@@ -1337,6 +1498,7 @@ typedef struct _krb5_lcc_data {
     ULONG  PackageId;
     char * cc_name;
     krb5_principal princ;
+    krb5_flags flags;
 } krb5_lcc_data;
 
 typedef struct _krb5_lcc_cursor {
@@ -1415,6 +1577,7 @@ krb5_lcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
     data = (krb5_lcc_data *)lid->data;    
     data->LogonHandle = LogonHandle;
     data->PackageId = PackageId;
+    data->princ = 0;
 
     data->cc_name = (char *)malloc(strlen(residual)+1);
     if (data->cc_name == NULL) {
@@ -1436,8 +1599,7 @@ krb5_lcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
 
         krb5_copy_principal(context, creds.client, &data->princ);
         krb5_free_cred_contents(context,&creds);
-    } else {
-        data->princ = 0;
+    } else if (!does_retrieve_ticket_cache_ticket()) {
         krb5_xfree(data->cc_name);
         krb5_xfree(lid->data);
         krb5_xfree(lid);
@@ -1454,7 +1616,9 @@ krb5_lcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
 }
 
 /*
- *  not supported
+ *  return success although we do not do anything
+ *  perhaps we could purge all existing tickets but that is 
+ *  probably not wise
  */
 static krb5_error_code KRB5_CALLCONV
 krb5_lcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
@@ -1462,7 +1626,7 @@ krb5_lcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
     if (!is_windows_2000())
         return KRB5_FCC_NOFILE;
 
-    return KRB5_CC_READONLY;
+    return KRB5_OK;
 }
 
 
@@ -1513,7 +1677,7 @@ krb5_lcc_destroy(krb5_context context, krb5_ccache id)
     if (id) { 
         data = (krb5_lcc_data *) id->data;
 
-        return PurgeMSTGT(data->LogonHandle, data->PackageId) ? KRB5_FCC_INTERNAL : KRB5_OK;
+               return PurgeAllTickets(data->LogonHandle, data->PackageId) ? KRB5_OK : KRB5_FCC_INTERNAL;
     }
     return KRB5_FCC_INTERNAL;
 }
@@ -1652,10 +1816,11 @@ krb5_lcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
     }
 
     /* convert the ticket */
-    if ( is_windows_xp() )
+    if ( is_windows_xp() ) {
         MSCredToMITCred(msticket, lcursor->response.xp->Tickets[lcursor->index-1].ClientRealm, context, creds);
-    else
+    } else {
         MSCredToMITCred(msticket, lcursor->mstgt->DomainName, context, creds);
+    }
     LsaFreeReturnBuffer(msticket);
     return KRB5_OK;
 }
@@ -1744,13 +1909,32 @@ krb5_lcc_get_name (krb5_context context, krb5_ccache id)
 static krb5_error_code KRB5_CALLCONV
 krb5_lcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
 {
+    krb5_lcc_data *data = (krb5_lcc_data *)id->data;
     krb5_error_code kret = KRB5_OK;
 
     if (!is_windows_2000())
         return KRB5_FCC_NOFILE;
 
     /* obtain principal */
-    return krb5_copy_principal(context, ((krb5_lcc_data *) id->data)->princ, princ);
+    if (data->princ)
+        return krb5_copy_principal(context, data->princ, princ);
+    else {
+        /*
+         * we must obtain a tgt from the cache in order to determine the principal
+         */
+        KERB_EXTERNAL_TICKET *msticket;
+        if (GetMSTGT(context, data->LogonHandle, data->PackageId, &msticket, FALSE)) {
+            /* convert the ticket */
+            krb5_creds creds;
+            MSCredToMITCred(msticket, msticket->DomainName, context, &creds);
+            LsaFreeReturnBuffer(msticket);
+
+            krb5_copy_principal(context, creds.client, &data->princ);
+            krb5_free_cred_contents(context,&creds);
+            return krb5_copy_principal(context, data->princ, princ);
+        }    
+    }
+    return KRB5_CC_NOTFOUND;
 }
 
      
@@ -1882,6 +2066,7 @@ krb5_lcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
     if (!is_windows_2000())
         return KRB5_FCC_NOFILE;
 
+    /* If not, lets try to obtain a matching ticket from the KDC */
     if ( creds->ticket_flags != 0 && creds->keyblock.enctype != 0 ) {
         /* if not, we must try to get a ticket without specifying any flags or etypes */
         krb5_copy_creds(context, creds, &creds_noflags);
@@ -1904,32 +2089,46 @@ krb5_lcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
 }
 
 /* 
- * The ability to remove a credential from the MS LSA cache cannot be implemented.
+ * Individual credentials can be implemented differently depending
+ * on the operating system version.  (undocumented.)
  * 
  * Errors:
  *    KRB5_CC_READONLY: 
  */
 static krb5_error_code KRB5_CALLCONV
-krb5_lcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
+krb5_lcc_remove_cred(krb5_context context, krb5_ccache id, krb5_flags flags,
                      krb5_creds *creds)
 {
+    krb5_lcc_data *data = (krb5_lcc_data *)id->data;
+
     if (!is_windows_2000())
         return KRB5_FCC_NOFILE;
 
+    if (!is_windows_xp()) {
+        if ( PurgeTicket2000( data->LogonHandle, data->PackageId, context, creds) )
+            return KRB5_OK;
+    } else {
+        if ( PurgeTicketXP( data->LogonHandle, data->PackageId, context, flags, creds) )
+            return KRB5_OK;
+    }
+
     return KRB5_CC_READONLY;
 }
 
 
 /*
  * Effects:
- *   None - ignored
+ *   Set
  */
 static krb5_error_code KRB5_CALLCONV
 krb5_lcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
 {
+    krb5_lcc_data *data = (krb5_lcc_data *)id->data;
+
     if (!is_windows_2000())
         return KRB5_FCC_NOFILE;
 
+    data->flags = flags;
     return KRB5_OK;
 }
 
@@ -1951,4 +2150,4 @@ const krb5_cc_ops krb5_lcc_ops = {
      krb5_lcc_remove_cred,
      krb5_lcc_set_flags
 };
-#endif /* _WIN32 */
\ No newline at end of file
+#endif /* _WIN32 */