diff options
-rw-r--r-- | src/lib/krb5/ccache/ChangeLog | 24 | ||||
-rw-r--r-- | src/lib/krb5/ccache/cc_mslsa.c | 245 |
2 files changed, 246 insertions, 23 deletions
diff --git a/src/lib/krb5/ccache/ChangeLog b/src/lib/krb5/ccache/ChangeLog index 034080d..bc7ca4f 100644 --- a/src/lib/krb5/ccache/ChangeLog +++ b/src/lib/krb5/ccache/ChangeLog @@ -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-07-25 Jeffrey Altman <jaltman@mit.edu> * cc_mslsa.c: is_windows_xp() should test for major version diff --git a/src/lib/krb5/ccache/cc_mslsa.c b/src/lib/krb5/ccache/cc_mslsa.c index a32359a..422426a 100644 --- a/src/lib/krb5/ccache/cc_mslsa.c +++ b/src/lib/krb5/ccache/cc_mslsa.c @@ -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 */ |