8240256: Better resource cleaning for SunPKCS11 Provider
Reviewed-by: valeriep
This commit is contained in:
parent
06f87cf441
commit
bdeaeb47d0
@ -144,7 +144,15 @@ final class Config {
|
||||
// how often to test for token insertion, if no token is present
|
||||
private int insertionCheckInterval = 2000;
|
||||
|
||||
// flag inidicating whether to omit the call to C_Initialize()
|
||||
// short ms value to indicate how often native cleaner thread is called
|
||||
private int resourceCleanerShortInterval = 2_000;
|
||||
// long ms value to indicate how often native cleaner thread is called
|
||||
private int resourceCleanerLongInterval = 60_000;
|
||||
|
||||
// should Token be destroyed after logout()
|
||||
private boolean destroyTokenAfterLogout;
|
||||
|
||||
// flag indicating whether to omit the call to C_Initialize()
|
||||
// should be used only if we are running within a process that
|
||||
// has already called it (e.g. Plugin inside of Mozilla/NSS)
|
||||
private boolean omitInitialize = false;
|
||||
@ -278,6 +286,18 @@ final class Config {
|
||||
return explicitCancel;
|
||||
}
|
||||
|
||||
boolean getDestroyTokenAfterLogout() {
|
||||
return destroyTokenAfterLogout;
|
||||
}
|
||||
|
||||
int getResourceCleanerShortInterval() {
|
||||
return resourceCleanerShortInterval;
|
||||
}
|
||||
|
||||
int getResourceCleanerLongInterval() {
|
||||
return resourceCleanerLongInterval;
|
||||
}
|
||||
|
||||
int getInsertionCheckInterval() {
|
||||
return insertionCheckInterval;
|
||||
}
|
||||
@ -412,6 +432,18 @@ final class Config {
|
||||
if (insertionCheckInterval < 100) {
|
||||
throw excLine(word + " must be at least 100 ms");
|
||||
}
|
||||
} else if (word.equals("cleaner.shortInterval")) {
|
||||
resourceCleanerShortInterval = parseIntegerEntry(word);
|
||||
if (resourceCleanerShortInterval < 1_000) {
|
||||
throw excLine(word + " must be at least 1000 ms");
|
||||
}
|
||||
} else if (word.equals("cleaner.longInterval")) {
|
||||
resourceCleanerLongInterval = parseIntegerEntry(word);
|
||||
if (resourceCleanerLongInterval < 1_000) {
|
||||
throw excLine(word + " must be at least 1000 ms");
|
||||
}
|
||||
} else if (word.equals("destroyTokenAfterLogout")) {
|
||||
destroyTokenAfterLogout = parseBooleanEntry(word);
|
||||
} else if (word.equals("showInfo")) {
|
||||
showInfo = parseBooleanEntry(word);
|
||||
} else if (word.equals("keyStoreCompatibilityMode")) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
@ -100,4 +100,8 @@ final class KeyCache {
|
||||
map.put(key, p11Key);
|
||||
}
|
||||
|
||||
synchronized void clear() {
|
||||
strongCache.clear();
|
||||
cacheReference = null;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ import sun.security.pkcs11.wrapper.*;
|
||||
import static sun.security.pkcs11.TemplateManager.O_GENERATE;
|
||||
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
|
||||
|
||||
import sun.security.util.Debug;
|
||||
import sun.security.util.DerValue;
|
||||
import sun.security.util.Length;
|
||||
import sun.security.util.ECUtil;
|
||||
@ -142,8 +141,8 @@ abstract class P11Key implements Key, Length {
|
||||
&& tokenLabel[2] == 'S');
|
||||
boolean extractKeyInfo = (!DISABLE_NATIVE_KEYS_EXTRACTION && isNSS &&
|
||||
extractable && !tokenObject);
|
||||
this.keyIDHolder = new NativeKeyHolder(this, keyID, session, extractKeyInfo,
|
||||
tokenObject);
|
||||
this.keyIDHolder = new NativeKeyHolder(this, keyID, session,
|
||||
extractKeyInfo, tokenObject);
|
||||
}
|
||||
|
||||
public long getKeyID() {
|
||||
@ -166,6 +165,18 @@ abstract class P11Key implements Key, Length {
|
||||
return (b == null) ? null : b.clone();
|
||||
}
|
||||
|
||||
// Called by the NativeResourceCleaner at specified intervals
|
||||
// See NativeResourceCleaner for more information
|
||||
static boolean drainRefQueue() {
|
||||
boolean found = false;
|
||||
SessionKeyRef next;
|
||||
while ((next = (SessionKeyRef) SessionKeyRef.refQueue.poll()) != null) {
|
||||
found = true;
|
||||
next.dispose();
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
abstract byte[] getEncodedInternal();
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
@ -882,7 +893,7 @@ abstract class P11Key implements Key, Length {
|
||||
return params;
|
||||
}
|
||||
public int hashCode() {
|
||||
if (token.isValid() == false) {
|
||||
if (!token.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
fetchValues();
|
||||
@ -891,7 +902,7 @@ abstract class P11Key implements Key, Length {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
// equals() should never throw exceptions
|
||||
if (token.isValid() == false) {
|
||||
if (!token.isValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof DHPrivateKey)) {
|
||||
@ -1129,7 +1140,6 @@ abstract class P11Key implements Key, Length {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class NativeKeyHolder {
|
||||
|
||||
private static long nativeKeyWrapperKeyID = 0;
|
||||
@ -1254,6 +1264,7 @@ final class NativeKeyHolder {
|
||||
this.ref = new SessionKeyRef(p11Key, keyID, wrapperKeyUsed,
|
||||
keySession);
|
||||
}
|
||||
|
||||
this.nativeKeyInfo = ((ki == null || ki.length == 0)? null : ki);
|
||||
}
|
||||
|
||||
@ -1327,24 +1338,9 @@ final class NativeKeyHolder {
|
||||
* still use these keys during finalization such as SSLSocket.
|
||||
*/
|
||||
final class SessionKeyRef extends PhantomReference<P11Key> {
|
||||
private static ReferenceQueue<P11Key> refQueue =
|
||||
new ReferenceQueue<P11Key>();
|
||||
static ReferenceQueue<P11Key> refQueue = new ReferenceQueue<>();
|
||||
private static Set<SessionKeyRef> refSet =
|
||||
Collections.synchronizedSet(new HashSet<SessionKeyRef>());
|
||||
|
||||
static ReferenceQueue<P11Key> referenceQueue() {
|
||||
return refQueue;
|
||||
}
|
||||
|
||||
private static void drainRefQueueBounded() {
|
||||
while (true) {
|
||||
SessionKeyRef next = (SessionKeyRef) refQueue.poll();
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
next.dispose();
|
||||
}
|
||||
}
|
||||
Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
// handle to the native key and the session it is generated under
|
||||
private long keyID;
|
||||
@ -1355,13 +1351,13 @@ final class SessionKeyRef extends PhantomReference<P11Key> {
|
||||
Session session) {
|
||||
super(p11Key, refQueue);
|
||||
if (session == null) {
|
||||
throw new ProviderException("key must be associated with a session");
|
||||
throw new ProviderException
|
||||
("key must be associated with a session");
|
||||
}
|
||||
registerNativeKey(keyID, session);
|
||||
this.wrapperKeyUsed = wrapperKeyUsed;
|
||||
|
||||
refSet.add(this);
|
||||
drainRefQueueBounded();
|
||||
}
|
||||
|
||||
void registerNativeKey(long newKeyID, Session newSession) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
@ -82,10 +82,6 @@ final class Session implements Comparable<Session> {
|
||||
return currentTime - lastAccess < MAX_IDLE_TIME;
|
||||
}
|
||||
|
||||
long idInternal() {
|
||||
return id;
|
||||
}
|
||||
|
||||
long id() {
|
||||
if (token.isPresent(this.id) == false) {
|
||||
throw new ProviderException("Token has been removed");
|
||||
@ -112,15 +108,40 @@ final class Session implements Comparable<Session> {
|
||||
return createdObjects.get() != 0;
|
||||
}
|
||||
|
||||
// regular close which will not close sessions when there are objects(keys)
|
||||
// still associated with them
|
||||
void close() {
|
||||
if (hasObjects()) {
|
||||
close(true);
|
||||
}
|
||||
|
||||
// forced close which will close sessions regardless if there are objects
|
||||
// associated with them. Note that closing the sessions this way may
|
||||
// lead to those associated objects(keys) un-usable. Thus should only be
|
||||
// used for scenarios such as the token is about to be removed, etc.
|
||||
void kill() {
|
||||
close(false);
|
||||
}
|
||||
|
||||
private void close(boolean checkObjCtr) {
|
||||
if (hasObjects() && checkObjCtr) {
|
||||
throw new ProviderException(
|
||||
"Internal error: close session with active objects");
|
||||
"Internal error: close session with active objects");
|
||||
}
|
||||
sessionRef.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the NativeResourceCleaner at specified intervals
|
||||
// See NativeResourceCleaner for more information
|
||||
static boolean drainRefQueue() {
|
||||
boolean found = false;
|
||||
SessionRef next;
|
||||
while ((next = (SessionRef) SessionRef.refQueue.poll())!= null) {
|
||||
found = true;
|
||||
next.dispose();
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* NOTE: Use PhantomReference here and not WeakReference
|
||||
* otherwise the sessions maybe closed before other objects
|
||||
@ -129,27 +150,10 @@ final class Session implements Comparable<Session> {
|
||||
final class SessionRef extends PhantomReference<Session>
|
||||
implements Comparable<SessionRef> {
|
||||
|
||||
private static ReferenceQueue<Session> refQueue =
|
||||
new ReferenceQueue<Session>();
|
||||
static ReferenceQueue<Session> refQueue = new ReferenceQueue<>();
|
||||
|
||||
private static Set<SessionRef> refList =
|
||||
Collections.synchronizedSortedSet(new TreeSet<SessionRef>());
|
||||
|
||||
static ReferenceQueue<Session> referenceQueue() {
|
||||
return refQueue;
|
||||
}
|
||||
|
||||
static int totalCount() {
|
||||
return refList.size();
|
||||
}
|
||||
|
||||
private static void drainRefQueueBounded() {
|
||||
while (true) {
|
||||
SessionRef next = (SessionRef) refQueue.poll();
|
||||
if (next == null) break;
|
||||
next.dispose();
|
||||
}
|
||||
}
|
||||
Collections.synchronizedSortedSet(new TreeSet<>());
|
||||
|
||||
// handle to the native session
|
||||
private long id;
|
||||
@ -160,8 +164,6 @@ final class SessionRef extends PhantomReference<Session>
|
||||
this.id = id;
|
||||
this.token = token;
|
||||
refList.add(this);
|
||||
// TBD: run at some interval and not every time?
|
||||
drainRefQueueBounded();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
@ -170,9 +172,7 @@ final class SessionRef extends PhantomReference<Session>
|
||||
if (token.isPresent(id)) {
|
||||
token.p11.C_CloseSession(id);
|
||||
}
|
||||
} catch (PKCS11Exception e1) {
|
||||
// ignore
|
||||
} catch (ProviderException e2) {
|
||||
} catch (PKCS11Exception | ProviderException e1) {
|
||||
// ignore
|
||||
} finally {
|
||||
this.clear();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
@ -26,11 +26,9 @@
|
||||
package sun.security.pkcs11;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import java.security.ProviderException;
|
||||
|
||||
import sun.security.util.Debug;
|
||||
|
||||
import sun.security.pkcs11.wrapper.*;
|
||||
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
|
||||
|
||||
@ -171,7 +169,9 @@ final class SessionManager {
|
||||
System.out.println("Killing session (" + location + ") active: "
|
||||
+ activeSessions.get());
|
||||
}
|
||||
closeSession(session);
|
||||
|
||||
session.kill();
|
||||
activeSessions.decrementAndGet();
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -179,7 +179,6 @@ final class SessionManager {
|
||||
if ((session == null) || (token.isValid() == false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (session.hasObjects()) {
|
||||
objSessions.release(session);
|
||||
} else {
|
||||
@ -188,6 +187,11 @@ final class SessionManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
void clearPools() {
|
||||
objSessions.closeAll();
|
||||
opSessions.closeAll();
|
||||
}
|
||||
|
||||
void demoteObjSession(Session session) {
|
||||
if (token.isValid() == false) {
|
||||
return;
|
||||
@ -196,6 +200,7 @@ final class SessionManager {
|
||||
System.out.println("Demoting session, active: " +
|
||||
activeSessions.get());
|
||||
}
|
||||
|
||||
boolean present = objSessions.remove(session);
|
||||
if (present == false) {
|
||||
// session is currently in use
|
||||
@ -238,6 +243,7 @@ final class SessionManager {
|
||||
private final SessionManager mgr;
|
||||
private final AbstractQueue<Session> pool;
|
||||
private final int SESSION_MAX = 5;
|
||||
private volatile boolean closed = false;
|
||||
|
||||
// Object session pools can contain unlimited sessions.
|
||||
// Operation session pools are limited and enforced by the queue.
|
||||
@ -260,7 +266,7 @@ final class SessionManager {
|
||||
|
||||
void release(Session session) {
|
||||
// Object session pools never return false, only Operation ones
|
||||
if (!pool.offer(session)) {
|
||||
if (closed || !pool.offer(session)) {
|
||||
mgr.closeSession(session);
|
||||
free();
|
||||
}
|
||||
@ -268,6 +274,9 @@ final class SessionManager {
|
||||
|
||||
// Free any old operation session if this queue is full
|
||||
void free() {
|
||||
// quick return path
|
||||
if (pool.size() == 0) return;
|
||||
|
||||
int n = SESSION_MAX;
|
||||
int i = 0;
|
||||
Session oldestSession;
|
||||
@ -291,6 +300,14 @@ final class SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
// empty out all sessions inside 'pool' and close them.
|
||||
// however the Pool can still accept sessions
|
||||
void closeAll() {
|
||||
closed = true;
|
||||
Session s;
|
||||
while ((s = pool.poll()) != null) {
|
||||
mgr.killSession(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,9 +38,7 @@ import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.ConfirmationCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.TextOutputCallback;
|
||||
|
||||
import com.sun.crypto.provider.ChaCha20Poly1305Parameters;
|
||||
|
||||
@ -88,6 +86,8 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
|
||||
private TokenPoller poller;
|
||||
|
||||
static NativeResourceCleaner cleaner;
|
||||
|
||||
Token getToken() {
|
||||
return token;
|
||||
}
|
||||
@ -907,13 +907,19 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
// background thread that periodically checks for token insertion
|
||||
// if no token is present. We need to do that in a separate thread because
|
||||
// the insertion check may block for quite a long time on some tokens.
|
||||
private static class TokenPoller implements Runnable {
|
||||
private static class TokenPoller extends Thread {
|
||||
private final SunPKCS11 provider;
|
||||
private volatile boolean enabled;
|
||||
|
||||
private TokenPoller(SunPKCS11 provider) {
|
||||
super((ThreadGroup)null, "Poller-" + provider.getName());
|
||||
setContextClassLoader(null);
|
||||
setDaemon(true);
|
||||
setPriority(Thread.MIN_PRIORITY);
|
||||
this.provider = provider;
|
||||
enabled = true;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
int interval = provider.config.getInsertionCheckInterval();
|
||||
while (enabled) {
|
||||
@ -942,13 +948,8 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
if (poller != null) {
|
||||
return;
|
||||
}
|
||||
final TokenPoller poller = new TokenPoller(this);
|
||||
Thread t = new Thread(null, poller, "Poller " + getName(), 0, false);
|
||||
t.setContextClassLoader(null);
|
||||
t.setDaemon(true);
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
t.start();
|
||||
this.poller = poller;
|
||||
poller = new TokenPoller(this);
|
||||
poller.start();
|
||||
}
|
||||
|
||||
// destroy the poller thread, if active
|
||||
@ -971,6 +972,56 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
return (token != null) && token.isValid();
|
||||
}
|
||||
|
||||
private class NativeResourceCleaner extends Thread {
|
||||
private long sleepMillis = config.getResourceCleanerShortInterval();
|
||||
private int count = 0;
|
||||
boolean keyRefFound, sessRefFound;
|
||||
|
||||
private NativeResourceCleaner() {
|
||||
super((ThreadGroup)null, "Cleanup-SunPKCS11");
|
||||
setContextClassLoader(null);
|
||||
setDaemon(true);
|
||||
setPriority(Thread.MIN_PRIORITY);
|
||||
}
|
||||
|
||||
/*
|
||||
* The cleaner.shortInterval and cleaner.longInterval properties
|
||||
* may be defined in the pkcs11 config file and are specified in milliseconds
|
||||
* Minimum value is 1000ms. Default values :
|
||||
* cleaner.shortInterval : 2000ms
|
||||
* cleaner.longInterval : 60000ms
|
||||
*
|
||||
* The cleaner thread runs at cleaner.shortInterval intervals
|
||||
* while P11Key or Session references continue to be found for cleaning.
|
||||
* If 100 iterations occur with no references being found, then the interval
|
||||
* period moves to cleaner.longInterval value. The cleaner thread moves back
|
||||
* to short interval checking if a resource is found
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
sleep(sleepMillis);
|
||||
} catch (InterruptedException ie) {
|
||||
break;
|
||||
}
|
||||
keyRefFound = P11Key.drainRefQueue();
|
||||
sessRefFound = Session.drainRefQueue();
|
||||
if (!keyRefFound && !sessRefFound) {
|
||||
count++;
|
||||
if (count > 100) {
|
||||
// no reference freed for some time
|
||||
// increase the sleep time
|
||||
sleepMillis = config.getResourceCleanerLongInterval();
|
||||
}
|
||||
} else {
|
||||
count = 0;
|
||||
sleepMillis = config.getResourceCleanerShortInterval();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// destroy the token. Called if we detect that it has been removed
|
||||
@SuppressWarnings("removal")
|
||||
synchronized void uninitToken(Token token) {
|
||||
@ -987,7 +1038,10 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
createPoller();
|
||||
// keep polling for token insertion unless configured not to
|
||||
if (removable && !config.getDestroyTokenAfterLogout()) {
|
||||
createPoller();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLegacy(CK_MECHANISM_INFO mechInfo)
|
||||
@ -1135,6 +1189,10 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
});
|
||||
|
||||
this.token = token;
|
||||
if (cleaner == null) {
|
||||
cleaner = new NativeResourceCleaner();
|
||||
cleaner.start();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class P11Service extends Service {
|
||||
@ -1350,12 +1408,12 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
("authProvider." + this.getName()));
|
||||
}
|
||||
|
||||
if (hasValidToken() == false) {
|
||||
if (!hasValidToken()) {
|
||||
throw new LoginException("No token present");
|
||||
|
||||
}
|
||||
|
||||
// see if a login is required
|
||||
|
||||
if ((token.tokenInfo.flags & CKF_LOGIN_REQUIRED) == 0) {
|
||||
if (debug != null) {
|
||||
debug.println("login operation not required for token - " +
|
||||
@ -1467,7 +1525,6 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
* this provider's <code>getName</code> method
|
||||
*/
|
||||
public void logout() throws LoginException {
|
||||
|
||||
if (!isConfigured()) {
|
||||
throw new IllegalStateException("Configuration is required");
|
||||
}
|
||||
@ -1494,10 +1551,13 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
}
|
||||
|
||||
try {
|
||||
if (token.isLoggedInNow(null) == false) {
|
||||
if (!token.isLoggedInNow(null)) {
|
||||
if (debug != null) {
|
||||
debug.println("user not logged in");
|
||||
}
|
||||
if (config.getDestroyTokenAfterLogout()) {
|
||||
token.destroy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (PKCS11Exception e) {
|
||||
@ -1505,7 +1565,6 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
}
|
||||
|
||||
// perform token logout
|
||||
|
||||
Session session = null;
|
||||
try {
|
||||
session = token.getOpSession();
|
||||
@ -1526,6 +1585,9 @@ public final class SunPKCS11 extends AuthProvider {
|
||||
throw le;
|
||||
} finally {
|
||||
token.releaseSession(session);
|
||||
if (config.getDestroyTokenAfterLogout()) {
|
||||
token.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,8 +292,12 @@ class Token implements Serializable {
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
valid = false;
|
||||
secretCache.clear();
|
||||
privateCache.clear();
|
||||
|
||||
sessionManager.clearPools();
|
||||
provider.uninitToken(this);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
Session getObjSession() throws PKCS11Exception {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2021, 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
|
||||
@ -21,15 +21,12 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
// common infrastructure for SunPKCS11 tests
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@ -110,11 +107,9 @@ public abstract class PKCS11Test {
|
||||
// for quick checking for generic testing than many if-else statements.
|
||||
static double softoken3_version = -1;
|
||||
static double nss3_version = -1;
|
||||
static Provider pkcs11;
|
||||
static Provider pkcs11 = newPKCS11Provider();
|
||||
|
||||
// Goes through ServiceLoader instead of Provider.getInstance() since it
|
||||
// works on all platforms
|
||||
static {
|
||||
public static Provider newPKCS11Provider() {
|
||||
ServiceLoader sl = ServiceLoader.load(java.security.Provider.class);
|
||||
Iterator<Provider> iter = sl.iterator();
|
||||
Provider p = null;
|
||||
@ -139,15 +134,20 @@ public abstract class PKCS11Test {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
pkcs11 = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Return a SunPKCS11 provider configured with the specified config file
|
||||
// Return the static test SunPKCS11 provider configured with the specified config file
|
||||
static Provider getSunPKCS11(String config) throws Exception {
|
||||
if (pkcs11 == null) {
|
||||
return getSunPKCS11(config, pkcs11);
|
||||
}
|
||||
|
||||
// Return the Provider p configured with the specified config file
|
||||
static Provider getSunPKCS11(String config, Provider p) throws Exception {
|
||||
if (p == null) {
|
||||
throw new NoSuchProviderException("No PKCS11 provider available");
|
||||
}
|
||||
return pkcs11.configure(config);
|
||||
return p.configure(config);
|
||||
}
|
||||
|
||||
public abstract void main(Provider p) throws Exception;
|
||||
@ -539,36 +539,45 @@ public abstract class PKCS11Test {
|
||||
nss_library = "nss3";
|
||||
}
|
||||
|
||||
// Run NSS testing on a Provider p configured with test nss config
|
||||
public static void testNSS(PKCS11Test test) throws Exception {
|
||||
String nssConfig = getNssConfig();
|
||||
if (nssConfig == null) {
|
||||
// issue loading libraries
|
||||
return;
|
||||
}
|
||||
Provider p = getSunPKCS11(nssConfig);
|
||||
test.premain(p);
|
||||
}
|
||||
|
||||
public static String getNssConfig() throws Exception {
|
||||
String libdir = getNSSLibDir();
|
||||
if (libdir == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
String base = getBase();
|
||||
|
||||
if (loadNSPR(libdir) == false) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
String base = getBase();
|
||||
|
||||
String libfile = libdir + System.mapLibraryName(nss_library);
|
||||
|
||||
String customDBdir = System.getProperty("CUSTOM_DB_DIR");
|
||||
String dbdir = (customDBdir != null) ?
|
||||
customDBdir :
|
||||
base + SEP + "nss" + SEP + "db";
|
||||
customDBdir :
|
||||
base + SEP + "nss" + SEP + "db";
|
||||
// NSS always wants forward slashes for the config path
|
||||
dbdir = dbdir.replace('\\', '/');
|
||||
|
||||
String customConfig = System.getProperty("CUSTOM_P11_CONFIG");
|
||||
String customConfigName = System.getProperty("CUSTOM_P11_CONFIG_NAME", "p11-nss.txt");
|
||||
String p11config = (customConfig != null) ?
|
||||
customConfig :
|
||||
base + SEP + "nss" + SEP + customConfigName;
|
||||
|
||||
System.setProperty("pkcs11test.nss.lib", libfile);
|
||||
System.setProperty("pkcs11test.nss.db", dbdir);
|
||||
Provider p = getSunPKCS11(p11config);
|
||||
test.premain(p);
|
||||
return (customConfig != null) ?
|
||||
customConfig :
|
||||
base + SEP + "nss" + SEP + customConfigName;
|
||||
}
|
||||
|
||||
// Generate a vector of supported elliptic curves of a given provider
|
||||
|
12
test/jdk/sun/security/pkcs11/Provider/MultipleLogins-nss.txt
Normal file
12
test/jdk/sun/security/pkcs11/Provider/MultipleLogins-nss.txt
Normal file
@ -0,0 +1,12 @@
|
||||
name = NSS
|
||||
|
||||
slot = 2
|
||||
|
||||
library = ${pkcs11test.nss.lib}
|
||||
|
||||
nssArgs = "configdir='${pkcs11test.nss.db}' certPrefix='' keyPrefix='' secmod='secmod.db' flags=readOnly"
|
||||
|
||||
destroyTokenAfterLogout = true
|
||||
cleaner.longInterval = 10000
|
||||
cleaner.shortInterval = 1000
|
||||
|
128
test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java
Normal file
128
test/jdk/sun/security/pkcs11/Provider/MultipleLogins.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import sun.security.pkcs11.SunPKCS11;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.security.KeyStore;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import jdk.test.lib.util.ForceGC;
|
||||
|
||||
public class MultipleLogins {
|
||||
private static final String KS_TYPE = "PKCS11";
|
||||
private static final int NUM_PROVIDERS = 20;
|
||||
private static final SunPKCS11[] providers = new SunPKCS11[NUM_PROVIDERS];
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int i =0; i < NUM_PROVIDERS; i++) {
|
||||
String nssConfig = PKCS11Test.getNssConfig();
|
||||
if (nssConfig == null) {
|
||||
throw new RuntimeException("issue setting up config");
|
||||
}
|
||||
providers[i] =
|
||||
(SunPKCS11)PKCS11Test.newPKCS11Provider()
|
||||
.configure(nssConfig);
|
||||
Security.addProvider(providers[i]);
|
||||
test(providers[i]);
|
||||
}
|
||||
|
||||
WeakReference<SunPKCS11>[] weakRef = new WeakReference[NUM_PROVIDERS];
|
||||
for (int i =0; i < NUM_PROVIDERS; i++) {
|
||||
weakRef[i] = new WeakReference<>(providers[i]);
|
||||
providers[i].logout();
|
||||
|
||||
if (i == 0) {
|
||||
// one provider stays for use with clean up thread
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
providers[i].login(new Subject(), new PasswordCallbackHandler());
|
||||
throw new RuntimeException("Expected LoginException");
|
||||
} catch (LoginException le) {
|
||||
// expected
|
||||
}
|
||||
|
||||
Security.removeProvider(providers[i].getName());
|
||||
providers[i] = null;
|
||||
|
||||
ForceGC gc = new ForceGC();
|
||||
int finalI = i;
|
||||
gc.await(() -> weakRef[finalI].get() == null);
|
||||
if (!weakRef[i].refersTo(null)) {
|
||||
throw new RuntimeException("Expected SunPKCS11 Provider to be GC'ed..");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(SunPKCS11 p) throws Exception {
|
||||
KeyStore ks = KeyStore.getInstance(KS_TYPE, p);
|
||||
|
||||
p.setCallbackHandler(new PasswordCallbackHandler());
|
||||
try {
|
||||
ks.load(null, (char[]) null);
|
||||
} catch (IOException e) {
|
||||
if (!e.getMessage().contains("load failed")) {
|
||||
// we expect the keystore load to fail
|
||||
throw new RuntimeException("unexpected exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
p.logout();
|
||||
|
||||
try {
|
||||
ks.load(null, (char[]) null);
|
||||
} catch (IOException e) {
|
||||
if (e.getCause() instanceof LoginException &&
|
||||
e.getCause().getMessage().contains("No token present")) {
|
||||
// expected
|
||||
} else {
|
||||
throw new RuntimeException("Token was present", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PasswordCallbackHandler implements CallbackHandler {
|
||||
public void handle(Callback[] callbacks)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (!(callbacks[0] instanceof PasswordCallback)) {
|
||||
throw new UnsupportedCallbackException(callbacks[0]);
|
||||
}
|
||||
PasswordCallback pc = (PasswordCallback)callbacks[0];
|
||||
pc.setPassword(null);
|
||||
}
|
||||
}
|
||||
}
|
135
test/jdk/sun/security/pkcs11/Provider/MultipleLogins.sh
Normal file
135
test/jdk/sun/security/pkcs11/Provider/MultipleLogins.sh
Normal file
@ -0,0 +1,135 @@
|
||||
#
|
||||
# Copyright (c) 2021, 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.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# @test
|
||||
# @bug 7777777
|
||||
# @summary
|
||||
# @library /test/lib/
|
||||
# @build jdk.test.lib.util.ForceGC
|
||||
# @run shell MultipleLogins.sh
|
||||
|
||||
# set a few environment variables so that the shell-script can run stand-alone
|
||||
# in the source directory
|
||||
|
||||
# if running by hand on windows, change TESTSRC and TESTCLASSES to "."
|
||||
if [ "${TESTSRC}" = "" ] ; then
|
||||
TESTSRC=`pwd`
|
||||
fi
|
||||
if [ "${TESTCLASSES}" = "" ] ; then
|
||||
TESTCLASSES=`pwd`
|
||||
fi
|
||||
|
||||
if [ "${TESTCLASSPATH}" = "" ] ; then
|
||||
TESTCLASSPATH=`pwd`
|
||||
fi
|
||||
|
||||
if [ "${COMPILEJAVA}" = "" ]; then
|
||||
COMPILEJAVA="${TESTJAVA}"
|
||||
fi
|
||||
echo TESTSRC=${TESTSRC}
|
||||
echo TESTCLASSES=${TESTCLASSES}
|
||||
echo TESTJAVA=${TESTJAVA}
|
||||
echo COMPILEJAVA=${COMPILEJAVA}
|
||||
echo ""
|
||||
|
||||
# let java test exit if platform unsupported
|
||||
|
||||
OS=`uname -s`
|
||||
case "$OS" in
|
||||
Linux )
|
||||
FS="/"
|
||||
PS=":"
|
||||
CP="${FS}bin${FS}cp"
|
||||
CHMOD="${FS}bin${FS}chmod"
|
||||
;;
|
||||
Darwin )
|
||||
FS="/"
|
||||
PS=":"
|
||||
CP="${FS}bin${FS}cp"
|
||||
CHMOD="${FS}bin${FS}chmod"
|
||||
;;
|
||||
AIX )
|
||||
FS="/"
|
||||
PS=":"
|
||||
CP="${FS}bin${FS}cp"
|
||||
CHMOD="${FS}bin${FS}chmod"
|
||||
;;
|
||||
Windows* )
|
||||
FS="\\"
|
||||
PS=";"
|
||||
CP="cp"
|
||||
CHMOD="chmod"
|
||||
;;
|
||||
CYGWIN* )
|
||||
FS="/"
|
||||
PS=";"
|
||||
CP="cp"
|
||||
CHMOD="chmod"
|
||||
#
|
||||
# javac does not like /cygdrive produced by `pwd`
|
||||
#
|
||||
TESTSRC=`cygpath -d ${TESTSRC}`
|
||||
;;
|
||||
* )
|
||||
echo "Unrecognized system!"
|
||||
exit 1;
|
||||
;;
|
||||
esac
|
||||
|
||||
# first make cert/key DBs writable
|
||||
|
||||
${CP} ${TESTSRC}${FS}..${FS}nss${FS}db${FS}cert8.db ${TESTCLASSES}
|
||||
${CHMOD} +w ${TESTCLASSES}${FS}cert8.db
|
||||
|
||||
${CP} ${TESTSRC}${FS}..${FS}nss${FS}db${FS}key3.db ${TESTCLASSES}
|
||||
${CHMOD} +w ${TESTCLASSES}${FS}key3.db
|
||||
|
||||
# compile test
|
||||
${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
|
||||
-classpath ${TESTCLASSPATH} \
|
||||
-d ${TESTCLASSES} \
|
||||
--add-modules jdk.crypto.cryptoki \
|
||||
--add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED \
|
||||
${TESTSRC}${FS}..${FS}..${FS}..${FS}..${FS}..${FS}lib${FS}jdk${FS}test${FS}lib${FS}artifacts${FS}*.java \
|
||||
${TESTSRC}${FS}MultipleLogins.java \
|
||||
${TESTSRC}${FS}..${FS}PKCS11Test.java
|
||||
|
||||
# run test
|
||||
${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \
|
||||
-classpath ${TESTCLASSPATH} \
|
||||
--add-modules jdk.crypto.cryptoki \
|
||||
--add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED \
|
||||
-DCUSTOM_DB_DIR=${TESTCLASSES} \
|
||||
-DCUSTOM_P11_CONFIG=${TESTSRC}${FS}MultipleLogins-nss.txt \
|
||||
-DNO_DEFAULT=true \
|
||||
-DNO_DEIMOS=true \
|
||||
-Dtest.src=${TESTSRC} \
|
||||
-Dtest.classes=${TESTCLASSES} \
|
||||
-Djava.security.debug=${DEBUG} \
|
||||
MultipleLogins
|
||||
|
||||
# save error status
|
||||
status=$?
|
||||
|
||||
# return
|
||||
exit $status
|
Loading…
Reference in New Issue
Block a user