/*++ Copyright (c) 1996 Microsoft Corporation Module Name: kerberos.cpp Abstract: Routines to read/write/configure kerberos policy settings The following modules have links to kerberos policy scejet.c inftojet.c pfget.c config.c analyze.c Author: Jin Huang (jinhuang) 17-Dec-1997 Revision History: jinhuang 28-Jan-1998 splitted to client-server --*/ #include "headers.h" #include "serverp.h" #include "kerberos.h" #include "kerbcon.h" #include "pfp.h" #define MAXDWORD 0xffffffff static PWSTR KerbItems[] = { {(PWSTR)TEXT("MaxTicketAge")}, {(PWSTR)TEXT("MaxRenewAge")}, {(PWSTR)TEXT("MaxServiceAge")}, {(PWSTR)TEXT("MaxClockSkew")}, {(PWSTR)TEXT("TicketValidateClient")} }; #define MAX_KERB_ITEMS 5 #define IDX_KERB_MAX 0 #define IDX_KERB_RENEW 1 #define IDX_KERB_SERVICE 2 #define IDX_KERB_CLOCK 3 #define IDX_KERB_VALIDATE 4 SCESTATUS ScepGetKerberosPolicy( IN PSCECONTEXT hProfile, IN SCETYPE ProfileType, OUT PSCE_KERBEROS_TICKET_INFO * ppKerberosInfo, OUT PSCE_ERROR_LOG_INFO *Errlog OPTIONAL ) /*++ Routine Description: This routine retrieves kerberos policy information from the Jet database and stores in the output buffer ppKerberosInfo. Arguments: hProfile - The profile handle context ppKerberosInfo - the output buffer to hold kerberos settings. Errlog - A buffer to hold all error codes/text encountered when parsing the INF file. If Errlog is NULL, no further error information is returned except the return DWORD Return value: SCESTATUS - SCESTATUS_SUCCESS SCESTATUS_NOT_ENOUGH_RESOURCE SCESTATUS_INVALID_PARAMETER SCESTATUS_BAD_FORMAT SCESTATUS_INVALID_DATA --*/ { SCESTATUS rc; PSCESECTION hSection=NULL; SCE_KEY_LOOKUP AccessKeys[] = { {(PWSTR)TEXT("MaxTicketAge"), offsetof(struct _SCE_KERBEROS_TICKET_INFO_, MaxTicketAge), 'D'}, {(PWSTR)TEXT("MaxRenewAge"), offsetof(struct _SCE_KERBEROS_TICKET_INFO_, MaxRenewAge), 'D'}, {(PWSTR)TEXT("MaxServiceAge"), offsetof(struct _SCE_KERBEROS_TICKET_INFO_, MaxServiceAge), 'D'}, {(PWSTR)TEXT("MaxClockSkew"), offsetof(struct _SCE_KERBEROS_TICKET_INFO_, MaxClockSkew), 'D'}, {(PWSTR)TEXT("TicketValidateClient"), offsetof(struct _SCE_KERBEROS_TICKET_INFO_, TicketValidateClient), 'D'} }; DWORD cKeys = sizeof(AccessKeys) / sizeof(SCE_KEY_LOOKUP); SCE_KERBEROS_TICKET_INFO TicketInfo; if ( ppKerberosInfo == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } rc = ScepGetFixValueSection( hProfile, szKerberosPolicy, AccessKeys, cKeys, ProfileType, (PVOID)&TicketInfo, &hSection, Errlog ); if ( rc != SCESTATUS_SUCCESS ) { return(rc); } // // copy the value in TicketInfo to ppKerberosInfo // if ( NULL == *ppKerberosInfo ) { *ppKerberosInfo = (PSCE_KERBEROS_TICKET_INFO)ScepAlloc(0, sizeof(SCE_KERBEROS_TICKET_INFO)); } if ( *ppKerberosInfo ) { memcpy(*ppKerberosInfo, &TicketInfo, sizeof(SCE_KERBEROS_TICKET_INFO)); } else { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; } SceJetCloseSection(&hSection, TRUE); return(rc); } #if _WIN32_WINNT>=0x0500 SCESTATUS ScepConfigureKerberosPolicy( IN PSCECONTEXT hProfile, IN PSCE_KERBEROS_TICKET_INFO pKerberosInfo, IN DWORD ConfigOptions ) /* ++ Routine Description: This routine configure the kerberos policy settings in the area of security policy. Arguments: pKerberosInfo - The buffer which contains kerberos policy settings Return value: SCESTATUS_SUCCESS SCESTATUS_NOT_ENOUGH_RESOURCE SCESTATUS_INVALID_PARAMETER SCESTATUS_OTHER_ERROR -- */ { if ( !pKerberosInfo ) { // // if no info to configure // return SCESTATUS_SUCCESS; } NTSTATUS NtStatus; LSA_HANDLE lsaHandle=NULL; DWORD rc = NO_ERROR; BOOL bDefaultUsed=FALSE; BOOL bDefined=FALSE; // // open LSA policy to configure kerberos policy // NtStatus = ScepOpenLsaPolicy( MAXIMUM_ALLOWED, &lsaHandle, TRUE ); if (!NT_SUCCESS(NtStatus)) { lsaHandle = NULL; rc = RtlNtStatusToDosError( NtStatus ); ScepLogOutput3( 1, rc, SCEDLL_LSA_POLICY); if ( ConfigOptions & SCE_RSOP_CALLBACK ) ScepRsopLog(SCE_RSOP_KERBEROS_INFO, rc, NULL, 0, 0); return(ScepDosErrorToSceStatus(rc)); } // // query current kerberos policy settings into pBuffer // PPOLICY_DOMAIN_KERBEROS_TICKET_INFO pBuffer=NULL; POLICY_DOMAIN_KERBEROS_TICKET_INFO TicketInfo; NtStatus = LsaQueryDomainInformationPolicy( lsaHandle, PolicyDomainKerberosTicketInformation, (PVOID *)&pBuffer ); if ( NT_SUCCESS(NtStatus) && pBuffer ) { // // transfer ticket info to TicketInfo buffer // TicketInfo.AuthenticationOptions = pBuffer->AuthenticationOptions; TicketInfo.MaxTicketAge = pBuffer->MaxTicketAge; TicketInfo.MaxRenewAge = pBuffer->MaxRenewAge; TicketInfo.MaxServiceTicketAge = pBuffer->MaxServiceTicketAge; TicketInfo.MaxClockSkew = pBuffer->MaxClockSkew; // // free the buffer // LsaFreeMemory((PVOID)pBuffer); } else { // // no kerberos policy is configured yet because by default it's not created. // let's create it now. set a default ticket info // TicketInfo.AuthenticationOptions = POLICY_KERBEROS_VALIDATE_CLIENT; TicketInfo.MaxTicketAge.QuadPart = (LONGLONG) KERBDEF_MAX_TICKET*60*60 * 10000000L; TicketInfo.MaxRenewAge.QuadPart = (LONGLONG) KERBDEF_MAX_RENEW*24*60*60 * 10000000L; TicketInfo.MaxServiceTicketAge.QuadPart = (LONGLONG) KERBDEF_MAX_SERVICE*60 * 10000000L; TicketInfo.MaxClockSkew.QuadPart = (LONGLONG) KERBDEF_MAX_CLOCK*60 * 10000000L; bDefaultUsed = TRUE; } pBuffer = &TicketInfo; // // process each field in pKerberosInfo // BOOL bFlagSet=FALSE; ULONG lOptions=0; ULONG lValue=0; SCE_TATTOO_KEYS *pTattooKeys=NULL; DWORD cTattooKeys=0; PSCESECTION hSectionDomain=NULL; PSCESECTION hSectionTattoo=NULL; #define MAX_KERB_KEYS 5 // // if in policy propagation, open the policy sections // since kerberos policy is only available on DCs and kerberos policy (account policy) // can't be reset to local settings on each DC, there is no point to query/save // the tattoo values // /* do not take tattoo value for kerberos if ( (ConfigOptions & SCE_POLICY_TEMPLATE) && hProfile ) { pTattooKeys = (SCE_TATTOO_KEYS *)ScepAlloc(LPTR,MAX_KERB_KEYS*sizeof(SCE_TATTOO_KEYS)); if ( !pTattooKeys ) { ScepLogOutput3(1, ERROR_NOT_ENOUGH_MEMORY, SCESRV_POLICY_TATTOO_ERROR_CREATE); } } */ if ( pKerberosInfo->MaxRenewAge != SCE_NO_VALUE ) { ScepTattooCheckAndUpdateArray(pTattooKeys, &cTattooKeys, (PWSTR)L"MaxRenewAge", ConfigOptions, KERBDEF_MAX_RENEW); } if ( pKerberosInfo->MaxRenewAge == SCE_FOREVER_VALUE ) { if ( pBuffer->MaxRenewAge.HighPart != MINLONG || pBuffer->MaxRenewAge.LowPart != 0 ) { // // Maximum LARGE_INTEGER .ie. never // pBuffer->MaxRenewAge.HighPart = MINLONG; pBuffer->MaxRenewAge.LowPart = 0; bFlagSet = TRUE; } bDefined = TRUE; } else if ( SCE_NO_VALUE != pKerberosInfo->MaxRenewAge ) { // // ticket is renewable, the max age is stored in MaxRenewAge // using days // lValue = (DWORD) (pBuffer->MaxRenewAge.QuadPart / (LONGLONG)(10000000L) ); lValue /= 3600; lValue /= 24; if ( lValue != pKerberosInfo->MaxRenewAge ) { pBuffer->MaxRenewAge.QuadPart = (LONGLONG)pKerberosInfo->MaxRenewAge*24*3600 * 10000000L; bFlagSet = TRUE; } bDefined = TRUE; } // // validate client ? // if ( pKerberosInfo->TicketValidateClient != SCE_NO_VALUE ) { if ( pKerberosInfo->TicketValidateClient ) { lOptions |= POLICY_KERBEROS_VALIDATE_CLIENT; } ScepTattooCheckAndUpdateArray(pTattooKeys, &cTattooKeys, (PWSTR)L"TicketValidateClient", ConfigOptions, KERBDEF_VALIDATE); if ( ( pBuffer->AuthenticationOptions & POLICY_KERBEROS_VALIDATE_CLIENT ) != ( lOptions & POLICY_KERBEROS_VALIDATE_CLIENT ) ) { pBuffer->AuthenticationOptions = lOptions; bFlagSet = TRUE; } bDefined = TRUE; } // // max ticket age // if ( pKerberosInfo->MaxTicketAge != SCE_NO_VALUE ) { ScepTattooCheckAndUpdateArray(pTattooKeys, &cTattooKeys, (PWSTR)L"MaxTicketAge", ConfigOptions, KERBDEF_MAX_TICKET); bDefined = TRUE; } if ( pKerberosInfo->MaxTicketAge == SCE_FOREVER_VALUE ) { if ( pBuffer->MaxTicketAge.HighPart != MINLONG || pBuffer->MaxTicketAge.LowPart != 0 ) { // // Maximum LARGE_INTEGER .ie. never // pBuffer->MaxTicketAge.HighPart = MINLONG; pBuffer->MaxTicketAge.LowPart = 0; bFlagSet = TRUE; } bDefined = TRUE; } else if ( pKerberosInfo->MaxTicketAge != SCE_NO_VALUE ) { // in hours lValue = (DWORD) (pBuffer->MaxTicketAge.QuadPart / (LONGLONG)(10000000L) ); lValue /= 3600; if ( lValue != pKerberosInfo->MaxTicketAge ) { pBuffer->MaxTicketAge.QuadPart = (LONGLONG)pKerberosInfo->MaxTicketAge*60*60 * 10000000L; bFlagSet = TRUE; } bDefined = TRUE; } // // max service ticket age // if ( pKerberosInfo->MaxServiceAge != SCE_NO_VALUE ) { ScepTattooCheckAndUpdateArray(pTattooKeys, &cTattooKeys, (PWSTR)L"MaxServiceAge", ConfigOptions, KERBDEF_MAX_SERVICE); bDefined = TRUE; } if ( pKerberosInfo->MaxServiceAge == SCE_FOREVER_VALUE ) { if ( pBuffer->MaxServiceTicketAge.HighPart != MINLONG || pBuffer->MaxServiceTicketAge.LowPart != 0 ) { // // Maximum LARGE_INTEGER .ie. never // pBuffer->MaxServiceTicketAge.HighPart = MINLONG; pBuffer->MaxServiceTicketAge.LowPart = 0; bFlagSet = TRUE; } bDefined = TRUE; } else if ( pKerberosInfo->MaxServiceAge != SCE_NO_VALUE ) { // in minutes lValue = (DWORD) (pBuffer->MaxServiceTicketAge.QuadPart / (LONGLONG)(10000000L) ); lValue /= 60; if ( lValue != pKerberosInfo->MaxServiceAge ) { pBuffer->MaxServiceTicketAge.QuadPart = (LONGLONG)pKerberosInfo->MaxServiceAge*60 * 10000000L; bFlagSet = TRUE; } bDefined = TRUE; } // // max clock // if ( pKerberosInfo->MaxClockSkew != SCE_NO_VALUE ) { ScepTattooCheckAndUpdateArray(pTattooKeys, &cTattooKeys, (PWSTR)L"MaxClockSkew", ConfigOptions, KERBDEF_MAX_CLOCK); bDefined = TRUE; } if ( pKerberosInfo->MaxClockSkew == SCE_FOREVER_VALUE ) { if ( pBuffer->MaxClockSkew.HighPart != MINLONG || pBuffer->MaxClockSkew.LowPart != 0 ) { // // Maximum LARGE_INTEGER .ie. never // pBuffer->MaxClockSkew.HighPart = MINLONG; pBuffer->MaxClockSkew.LowPart = 0; bFlagSet = TRUE; } bDefined = TRUE; } else if ( pKerberosInfo->MaxClockSkew != SCE_NO_VALUE ) { // in minutes lValue = (DWORD) (pBuffer->MaxClockSkew.QuadPart / (LONGLONG)(10000000L) ); lValue /= 60; if ( lValue != pKerberosInfo->MaxClockSkew ) { pBuffer->MaxClockSkew.QuadPart = (LONGLONG)pKerberosInfo->MaxClockSkew*60 * 10000000L; bFlagSet = TRUE; } bDefined = TRUE; } if ( bFlagSet || (bDefaultUsed && bDefined) ) { // // if anything for kerberos to configure // NtStatus = LsaSetDomainInformationPolicy( lsaHandle, PolicyDomainKerberosTicketInformation, (PVOID)pBuffer ); rc = RtlNtStatusToDosError( NtStatus ); if ( rc != NO_ERROR ) { ScepLogOutput3(1, rc, SCEDLL_SCP_ERROR_KERBEROS); } else { ScepLogOutput3(1, 0, SCEDLL_SCP_KERBEROS); } } if ( (ConfigOptions & SCE_POLICY_TEMPLATE) && hProfile && pTattooKeys && cTattooKeys ) { ScepTattooOpenPolicySections( hProfile, szKerberosPolicy, &hSectionDomain, &hSectionTattoo ); ScepLogOutput3(3,0,SCESRV_POLICY_TATTOO_ARRAY,cTattooKeys); // // some policy is different than the system setting // check if we should save the existing setting as the tattoo value // also remove reset'ed tattoo policy // ScepTattooManageValues(hSectionDomain, hSectionTattoo, pTattooKeys, cTattooKeys, rc); if ( hSectionDomain ) SceJetCloseSection(&hSectionDomain,TRUE); if ( hSectionTattoo ) SceJetCloseSection(&hSectionTattoo,TRUE); } if ( pTattooKeys ) ScepFree(pTattooKeys); if ( ConfigOptions & SCE_RSOP_CALLBACK ) ScepRsopLog(SCE_RSOP_KERBEROS_INFO, rc, NULL, 0, 0); // // close LSA policy // LsaClose( lsaHandle ); return(ScepDosErrorToSceStatus(rc)); } SCESTATUS ScepAnalyzeKerberosPolicy( IN PSCECONTEXT hProfile OPTIONAL, IN PSCE_KERBEROS_TICKET_INFO pKerInfo, IN DWORD Options ) /* ++ Routine Description: This routine queries the system kerberos policy settings and compare them with the template settings. Arguments: hProfile - the profile context pKerInfo - The buffer which contains kerberos settings to compare with or the buffer to query system settings into Options - the option(s) for the analysis, e.g., SCE_SYSTEM_SETTINGS Return value: -- */ { NTSTATUS NtStatus; LSA_HANDLE lsaHandle=NULL; DWORD rc32 = NO_ERROR; SCESTATUS rc=SCESTATUS_SUCCESS; PPOLICY_DOMAIN_KERBEROS_TICKET_INFO pBuffer=NULL; DWORD dValue; PSCESECTION hSection=NULL; POLICY_DOMAIN_KERBEROS_TICKET_INFO KerbTicketInfo; if ( !pKerInfo ) { // // if no template info, do not analyze // if ( Options & SCE_SYSTEM_SETTINGS ) { return SCESTATUS_INVALID_PARAMETER; } else { return SCESTATUS_SUCCESS; } } // // open LSA policy to configure kerberos policy // NtStatus = ScepOpenLsaPolicy( MAXIMUM_ALLOWED, &lsaHandle, TRUE ); if (!NT_SUCCESS(NtStatus)) { lsaHandle = NULL; rc32 = RtlNtStatusToDosError( NtStatus ); ScepLogOutput3( 1, rc32, SCEDLL_LSA_POLICY); return(ScepDosErrorToSceStatus(rc32)); } if ( !(Options & SCE_SYSTEM_SETTINGS) ) { // // Prepare kerberos section // rc = ScepStartANewSection( hProfile, &hSection, (Options & SCE_GENERATE_ROLLBACK) ? SCEJET_TABLE_SMP : SCEJET_TABLE_SAP, szKerberosPolicy ); } if ( rc != SCESTATUS_SUCCESS ) { ScepLogOutput3(1, ScepSceStatusToDosError(rc), SCEDLL_SAP_START_SECTION, (PWSTR)szKerberosPolicy); } else { DWORD KerbValues[MAX_KERB_ITEMS]; for ( dValue=0; dValueMaxTicketAge.HighPart == MINLONG && pBuffer->MaxTicketAge.LowPart == 0 ) { // // Maximum password age value is MINLONG,0 // dValue = SCE_FOREVER_VALUE; } else { dValue = (DWORD) ( pBuffer->MaxTicketAge.QuadPart / (LONGLONG)(10000000L) ); // // using hours // // dValue /= 24; dValue /= 3600; } rc = SCESTATUS_SUCCESS; if ( Options & SCE_SYSTEM_SETTINGS ) { pKerInfo->MaxTicketAge = dValue; } else { rc = ScepCompareAndSaveIntValue( hSection, L"MaxTicketAge", (Options & SCE_GENERATE_ROLLBACK), pKerInfo->MaxTicketAge, dValue); } if ( SCESTATUS_SUCCESS == rc ) { KerbValues[IDX_KERB_MAX] = 1; if ( pBuffer->MaxRenewAge.HighPart == MINLONG && pBuffer->MaxRenewAge.LowPart == 0 ) { // // Maximum age value is MINLONG,0 // dValue = SCE_FOREVER_VALUE; } else { dValue = (DWORD) ( pBuffer->MaxRenewAge.QuadPart / (LONGLONG)(10000000L) ); // // using days // dValue /= 3600; dValue /= 24; } if ( Options & SCE_SYSTEM_SETTINGS ) { pKerInfo->MaxRenewAge = dValue; } else { rc = ScepCompareAndSaveIntValue( hSection, L"MaxRenewAge", (Options & SCE_GENERATE_ROLLBACK), pKerInfo->MaxRenewAge, dValue); } if ( SCESTATUS_SUCCESS == rc ) { KerbValues[IDX_KERB_RENEW] = 1; if ( pBuffer->MaxServiceTicketAge.HighPart == MINLONG && pBuffer->MaxServiceTicketAge.LowPart == 0 ) { // // Maximum age value is MINLONG,0 // dValue = SCE_FOREVER_VALUE; } else { dValue = (DWORD) ( pBuffer->MaxServiceTicketAge.QuadPart / (LONGLONG)(10000000L) ); // // using minutes // dValue /= 60; } if ( Options & SCE_SYSTEM_SETTINGS ) { pKerInfo->MaxServiceAge = dValue; } else { rc = ScepCompareAndSaveIntValue( hSection, L"MaxServiceAge", (Options & SCE_GENERATE_ROLLBACK), pKerInfo->MaxServiceAge, dValue); } if ( SCESTATUS_SUCCESS == rc ) { KerbValues[IDX_KERB_SERVICE] = 1; if ( pBuffer->MaxClockSkew.HighPart == MINLONG && pBuffer->MaxClockSkew.LowPart == 0 ) { // // Maximum age value is MINLONG,0 // dValue = SCE_FOREVER_VALUE; } else { dValue = (DWORD) ( pBuffer->MaxClockSkew.QuadPart / (LONGLONG)(10000000L) ); // // using minutes // dValue /= 60; } if ( Options & SCE_SYSTEM_SETTINGS ) { pKerInfo->MaxClockSkew = dValue; } else { rc = ScepCompareAndSaveIntValue( hSection, L"MaxClockSkew", (Options & SCE_GENERATE_ROLLBACK), pKerInfo->MaxClockSkew, dValue); } if ( SCESTATUS_SUCCESS == rc ) { KerbValues[IDX_KERB_CLOCK] = 1; // // validate client // dValue = ( pBuffer->AuthenticationOptions & POLICY_KERBEROS_VALIDATE_CLIENT ) ? 1 : 0; if ( Options & SCE_SYSTEM_SETTINGS ) { pKerInfo->TicketValidateClient = dValue; } else { rc = ScepCompareAndSaveIntValue( hSection, L"TicketValidateClient", (Options & SCE_GENERATE_ROLLBACK), pKerInfo->TicketValidateClient, dValue); } if ( SCESTATUS_SUCCESS == rc ) { KerbValues[IDX_KERB_VALIDATE] = 1; } } } } } if ( !(Options & SCE_SYSTEM_SETTINGS) ) { if ( rc == SCESTATUS_SUCCESS ) { ScepLogOutput3( 1, 0, SCEDLL_SAP_KERBEROS); } else { ScepLogOutput3( 1, ScepSceStatusToDosError(rc), SCEDLL_SAP_ERROR_KERBEROS); } } if ( pBuffer != &KerbTicketInfo ) { // // free the buffer // LsaFreeMemory((PVOID)pBuffer); } } if ( !(Options & SCE_SYSTEM_SETTINGS) ) { if ( SCESTATUS_SUCCESS != rc && !(Options & SCE_GENERATE_ROLLBACK) ) { for ( dValue=0; dValue