1 /*
   2  * Copyright (c) 2019, Red Hat, Inc.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.krb5.internal;
  27 
  28 import java.util.Date;
  29 import java.util.HashMap;
  30 import java.util.LinkedList;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.Map.Entry;
  34 
  35 import sun.security.krb5.Credentials;
  36 import sun.security.krb5.PrincipalName;
  37 
  38 final class ReferralsCache {
  39 
  40     private static Map<PrincipalName, Map<String, ReferralCacheEntry>> referralsMap =
  41             new HashMap<>();
  42 
  43     static final class ReferralCacheEntry {
  44         private final Credentials creds;
  45         private final String toRealm;
  46         ReferralCacheEntry(Credentials creds, String toRealm) {
  47             this.creds = creds;
  48             this.toRealm = toRealm;
  49         }
  50         Credentials getCreds() {
  51             return creds;
  52         }
  53         String getToRealm() {
  54             return toRealm;
  55         }
  56     }
  57 
  58     static synchronized void put(PrincipalName service,
  59             String fromRealm, String toRealm, Credentials creds) {
  60         pruneExpired(service);
  61         if (creds.getEndTime().before(new Date())) {
  62             return;
  63         }
  64         Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
  65         if (entries == null) {
  66             entries = new HashMap<String, ReferralCacheEntry>();
  67             referralsMap.put(service, entries);
  68         }
  69         entries.remove(fromRealm);
  70         ReferralCacheEntry newEntry = new ReferralCacheEntry(creds, toRealm);
  71         entries.put(fromRealm, newEntry);
  72 
  73         // Remove loops within the cache
  74         ReferralCacheEntry current = newEntry;
  75         List<ReferralCacheEntry> seen = new LinkedList<>();
  76         while (current != null) {
  77             if (seen.contains(current)) {
  78                 // Loop found. Remove the first referral to cut the loop.
  79                 entries.remove(newEntry.getToRealm());
  80                 break;
  81             }
  82             seen.add(current);
  83             current = entries.get(current.getToRealm());
  84         }
  85     }
  86 
  87     static synchronized ReferralCacheEntry get(PrincipalName service,
  88             String fromRealm) {
  89         pruneExpired(service);
  90         Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
  91         if (entries != null) {
  92             ReferralCacheEntry toRef = entries.get(fromRealm);
  93             if (toRef != null) {
  94                 return toRef;
  95             }
  96         }
  97         return null;
  98     }
  99 
 100     private static void pruneExpired(PrincipalName service) {
 101         Date now = new Date();
 102         Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
 103         if (entries != null) {
 104             for (Entry<String, ReferralCacheEntry> mapEntry :
 105                     entries.entrySet()) {
 106                 if (mapEntry.getValue().getCreds().getEndTime().before(now)) {
 107                     entries.remove(mapEntry.getKey());
 108                 }
 109             }
 110         }
 111     }
 112 }