/* |
|
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
/* |
|
* |
|
* (C) Copyright IBM Corp. 1999 All Rights Reserved. |
|
* Copyright 1997 The Open Group Research Institute. All rights reserved. |
|
*/ |
|
package sun.security.krb5.internal.rcache; |
|
import sun.security.krb5.internal.Krb5; |
|
import java.util.Iterator; |
|
import java.util.LinkedList; |
|
import java.util.ListIterator; |
|
import sun.security.krb5.internal.KerberosTime; |
|
import sun.security.krb5.internal.KrbApErrException; |
|
/** |
|
* This class provides an efficient caching mechanism to store AuthTimeWithHash |
|
* from client authenticators. The cache minimizes the memory usage by doing |
|
* self-cleanup of expired items in the cache. |
|
* |
|
* AuthTimeWithHash objects inside a cache are always sorted from big (new) to |
|
* small (old) as determined by {@link AuthTimeWithHash#compareTo}. In the most |
|
* common case a newcomer should be newer than the first element. |
|
* |
|
* @author Yanni Zhang |
|
*/ |
|
public class AuthList { |
|
private final LinkedList<AuthTimeWithHash> entries; |
|
private final int lifespan; |
|
// entries.getLast().ctime, updated after each cleanup. |
|
private volatile int oldestTime = Integer.MIN_VALUE; |
|
/** |
|
* Constructs a AuthList. |
|
*/ |
|
public AuthList(int lifespan) { |
|
this.lifespan = lifespan; |
|
entries = new LinkedList<>(); |
|
} |
|
/** |
|
* Puts the authenticator timestamp into the cache in descending order, |
|
* and throw an exception if it's already there. |
|
*/ |
|
public synchronized void put(AuthTimeWithHash t, KerberosTime currentTime) |
|
throws KrbApErrException { |
|
if (entries.isEmpty()) { |
|
entries.addFirst(t); |
|
oldestTime = t.ctime; |
|
return; |
|
} else { |
|
AuthTimeWithHash temp = entries.getFirst(); |
|
int cmp = temp.compareTo(t); |
|
if (cmp < 0) { |
|
// This is the most common case, newly received authenticator |
|
// has larger timestamp. |
|
entries.addFirst(t); |
|
} else if (cmp == 0) { |
|
throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); |
|
} else { |
|
//unless client clock being re-adjusted. |
|
ListIterator<AuthTimeWithHash> it = entries.listIterator(1); |
|
boolean found = false; |
|
while (it.hasNext()) { |
|
temp = it.next(); |
|
cmp = temp.compareTo(t); |
|
if (cmp < 0) { |
|
// Find an older one, put in front of it |
|
entries.add(entries.indexOf(temp), t); |
|
found = true; |
|
break; |
|
} else if (cmp == 0) { |
|
throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); |
|
} |
|
} |
|
if (!found) { |
|
// All is newer than the newcomer. Sigh. |
|
entries.addLast(t); |
|
} |
|
} |
|
} |
|
// let us cleanup while we are here |
|
long timeLimit = currentTime.getSeconds() - lifespan; |
|
// Only trigger a cleanup when the earliest entry is |
|
// lifespan + 5 sec ago. This ensures a cleanup is done |
|
// at most every 5 seconds so that we don't always |
|
// addLast(removeLast). |
|
if (oldestTime > timeLimit - 5) { |
|
return; |
|
} |
|
// and we remove the *enough* old ones (1 lifetime ago) |
|
while (!entries.isEmpty()) { |
|
AuthTimeWithHash removed = entries.removeLast(); |
|
if (removed.ctime >= timeLimit) { |
|
entries.addLast(removed); |
|
oldestTime = removed.ctime; |
|
return; |
|
} |
|
} |
|
oldestTime = Integer.MIN_VALUE; |
|
} |
|
public boolean isEmpty() { |
|
return entries.isEmpty(); |
|
} |
|
public String toString() { |
|
StringBuilder sb = new StringBuilder(); |
|
Iterator<AuthTimeWithHash> iter = entries.descendingIterator(); |
|
int pos = entries.size(); |
|
while (iter.hasNext()) { |
|
AuthTimeWithHash at = iter.next(); |
|
sb.append('#').append(pos--).append(": ") |
|
.append(at.toString()).append('\n'); |
|
} |
|
return sb.toString(); |
|
} |
|
} |