Path: blob/master/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java
67745 views
/*1* Copyright (c) 2019, 2021, Red Hat, Inc.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.krb5.internal;2627import java.util.Arrays;28import java.util.Date;29import java.util.HashMap;30import java.util.Iterator;31import java.util.LinkedList;32import java.util.List;33import java.util.Map;34import java.util.Map.Entry;35import java.util.Objects;3637import sun.security.krb5.Credentials;38import sun.security.krb5.PrincipalName;3940/*41* ReferralsCache class implements a cache scheme for referral TGTs as42* described in RFC 6806 - 10. Caching Information. The goal is to optimize43* resources (such as network traffic) when a client requests credentials for a44* service principal to a given KDC. If a referral TGT was previously received,45* cached information is used instead of issuing a new query. Once a referral46* TGT expires, the corresponding referral entry in the cache is removed.47*/48final class ReferralsCache {4950private static Map<ReferralCacheKey, Map<String, ReferralCacheEntry>>51referralsMap = new HashMap<>();5253private static final class ReferralCacheKey {54private PrincipalName cname;55private PrincipalName sname;56private PrincipalName user; // S4U2Self only57private byte[] userSvcTicketEnc; // S4U2Proxy only58ReferralCacheKey (PrincipalName cname, PrincipalName sname,59PrincipalName user, Ticket userSvcTicket) {60this.cname = cname;61this.sname = sname;62this.user = user;63if (userSvcTicket != null && userSvcTicket.encPart != null) {64byte[] userSvcTicketEnc = userSvcTicket.encPart.getBytes();65if (userSvcTicketEnc.length > 0) {66this.userSvcTicketEnc = userSvcTicketEnc;67}68}69}70public boolean equals(Object other) {71if (!(other instanceof ReferralCacheKey))72return false;73ReferralCacheKey that = (ReferralCacheKey)other;74return cname.equals(that.cname) &&75sname.equals(that.sname) &&76Objects.equals(user, that.user) &&77Arrays.equals(userSvcTicketEnc, that.userSvcTicketEnc);78}79public int hashCode() {80return cname.hashCode() + sname.hashCode() +81Objects.hashCode(user) +82Arrays.hashCode(userSvcTicketEnc);83}84}8586static final class ReferralCacheEntry {87private final Credentials creds;88private final String toRealm;89ReferralCacheEntry(Credentials creds, String toRealm) {90this.creds = creds;91this.toRealm = toRealm;92}93Credentials getCreds() {94return creds;95}96String getToRealm() {97return toRealm;98}99}100101/*102* Add a new referral entry to the cache, including: client principal,103* service principal, user principal (S4U2Self only), client service104* ticket (S4U2Proxy only), source KDC realm, destination KDC realm and105* referral TGT.106*107* If a loop is generated when adding the new referral, the first hop is108* automatically removed. For example, let's assume that adding a109* REALM-3.COM -> REALM-1.COM referral generates the following loop:110* REALM-1.COM -> REALM-2.COM -> REALM-3.COM -> REALM-1.COM. Then,111* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.112*/113static synchronized void put(PrincipalName cname, PrincipalName service,114PrincipalName user, Ticket[] userSvcTickets, String fromRealm,115String toRealm, Credentials creds) {116Ticket userSvcTicket = (userSvcTickets != null ?117userSvcTickets[0] : null);118ReferralCacheKey k = new ReferralCacheKey(cname, service,119user, userSvcTicket);120pruneExpired(k);121if (creds.getEndTime().before(new Date())) {122return;123}124Map<String, ReferralCacheEntry> entries = referralsMap.get(k);125if (entries == null) {126entries = new HashMap<String, ReferralCacheEntry>();127referralsMap.put(k, entries);128}129entries.remove(fromRealm);130ReferralCacheEntry newEntry = new ReferralCacheEntry(creds, toRealm);131entries.put(fromRealm, newEntry);132133// Remove loops within the cache134ReferralCacheEntry current = newEntry;135List<ReferralCacheEntry> seen = new LinkedList<>();136while (current != null) {137if (seen.contains(current)) {138// Loop found. Remove the first referral to cut the loop.139entries.remove(newEntry.getToRealm());140break;141}142seen.add(current);143current = entries.get(current.getToRealm());144}145}146147/*148* Obtain a referral entry from the cache given a client principal,149* a service principal, a user principal (S4U2Self only), a client150* service ticket (S4U2Proxy only) and a source KDC realm.151*/152static synchronized ReferralCacheEntry get(PrincipalName cname,153PrincipalName service, PrincipalName user,154Ticket[] userSvcTickets, String fromRealm) {155Ticket userSvcTicket = (userSvcTickets != null ?156userSvcTickets[0] : null);157ReferralCacheKey k = new ReferralCacheKey(cname, service,158user, userSvcTicket);159pruneExpired(k);160Map<String, ReferralCacheEntry> entries = referralsMap.get(k);161if (entries != null) {162ReferralCacheEntry toRef = entries.get(fromRealm);163if (toRef != null) {164return toRef;165}166}167return null;168}169170/*171* Remove referral entries from the cache when referral TGTs expire.172*/173private static void pruneExpired(ReferralCacheKey k) {174Date now = new Date();175Map<String, ReferralCacheEntry> entries = referralsMap.get(k);176if (entries != null) {177Iterator<Entry<String, ReferralCacheEntry>> it = entries.entrySet().iterator();178while (it.hasNext()) {179Entry<String, ReferralCacheEntry> mapEntry = it.next();180if (mapEntry.getValue().getCreds().getEndTime().before(now)) {181it.remove();182}183}184}185}186}187188189