Skip to content

Commit

Permalink
Merge pull request #21883 from Zech-Hein/ltpa-token-changes
Browse files Browse the repository at this point in the history
changes to handling LTPAToken2
  • Loading branch information
Zech-Hein committed Aug 9, 2022
2 parents 1a8927a + 3ab992d commit f9dafab
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 22 deletions.
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2011 IBM Corporation and others.
Copyright (c) 2011, 2022 IBM Corporation and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
Expand Down Expand Up @@ -31,6 +31,8 @@
ibm:type="pid" ibm:reference="com.ibm.ws.security.authentication.filter" required="false" type="String" />
<AD id="authenticationFilter.target" name="internal" description="internal use only"
required="false" type="String" default="(service.pid=${authFilterRef})" />
<AD id="expirationDifferenceAllowed" name="internal" description="internal use only"
required="false" type="String" ibm:type="duration(ms)" default="3000ms" />
</OCD>

<Designate pid="com.ibm.ws.security.token.ltpa.LTPAConfiguration">
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2020 IBM Corporation and others.
* Copyright (c) 2012, 2022 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -64,8 +64,13 @@ public interface LTPAConfiguration {
long getTokenExpiration();

/**
* @return authFiler reference
* @return authFilter reference
*/
String getAuthFilterRef();

/**
* @return Maximum expiration difference allowed
*/
long getExpirationDifferenceAllowed();

}
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2020 IBM Corporation and others.
* Copyright (c) 2012, 2022 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -61,6 +61,7 @@ public class LTPAConfigurationImpl implements LTPAConfiguration, FileBasedAction
static final String DEFAULT_CONFIG_LOCATION = "${server.config.dir}/resources/security/ltpa.keys";
static final String DEFAULT_OUTPUT_LOCATION = "${server.output.dir}/resources/security/ltpa.keys";
static final String KEY_AUTH_FILTER_REF = "authFilterRef";
static final String KEY_EXP_DIFF_ALLOWED = "expirationDifferenceAllowed";
static protected final String KEY_SERVICE_PID = "service.pid";
private final AtomicServiceReference<WsLocationAdmin> locationService = new AtomicServiceReference<WsLocationAdmin>(KEY_LOCATION_SERVICE);
private final AtomicServiceReference<ExecutorService> executorService = new AtomicServiceReference<ExecutorService>(KEY_EXECUTOR_SERVICE);
Expand All @@ -81,6 +82,7 @@ public class LTPAConfigurationImpl implements LTPAConfiguration, FileBasedAction
private final WriteLock writeLock = reentrantReadWriteLock.writeLock();
private final ReadLock readLock = reentrantReadWriteLock.readLock();
private String authFilterRef;
private long expirationDifferenceAllowed;

protected void setExecutorService(ServiceReference<ExecutorService> ref) {
executorService.setReference(ref);
Expand Down Expand Up @@ -123,6 +125,9 @@ private void loadConfig(Map<String, Object> props) {
keyTokenExpiration = (Long) props.get(CFG_KEY_TOKEN_EXPIRATION);
monitorInterval = (Long) props.get(CFG_KEY_MONITOR_INTERVAL);
authFilterRef = (String) props.get(KEY_AUTH_FILTER_REF);
// expirationDifferenceAllowed is set to 3 seconds (3000ms) by default.
// If expirationDifferenceAllowed is set to less than 0, then the two expiration values will not be compared in the LTPAToken2.decrypt() method.
expirationDifferenceAllowed = (Long) props.get(KEY_EXP_DIFF_ALLOWED);
if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
Tr.debug(tc, "authFilterRef: " + authFilterRef);
}
Expand Down Expand Up @@ -206,8 +211,8 @@ public void restore() {
* When the configuration is modified,
*
* <pre>
* 1. If file name and expiration changed,
* then remove the file monitor registration and reload LTPA keys.
* 1. If file name, or expiration, or expirationDifferenceAllowed changed,
* then remove the file monitor registration, reload LTPA keys, and setup Runtime LTPA Infrastructure.
* 2. Else if only the monitor interval changed,
* then remove the file monitor registration and optionally create a new file monitor.
* 3. (Implicit)Else if only the key password changed,
Expand All @@ -218,10 +223,11 @@ protected void modified(Map<String, Object> props) {
String oldKeyImportFile = keyImportFile;
Long oldKeyTokenExpiration = keyTokenExpiration;
Long oldMonitorInterval = monitorInterval;
Long oldExpirationDifferenceAllowed = expirationDifferenceAllowed;

loadConfig(props);

if (isKeysConfigChanged(oldKeyImportFile, oldKeyTokenExpiration)) {
if (isKeysConfigChanged(oldKeyImportFile, oldKeyTokenExpiration, oldExpirationDifferenceAllowed)) {
unsetFileMonitorRegistration();
Tr.audit(tc, "LTPA_KEYS_TO_LOAD", keyImportFile);
setupRuntimeLTPAInfrastructure();
Expand All @@ -235,8 +241,8 @@ protected void modified(Map<String, Object> props) {
* The keys config is changed if the file or expiration were modified.
* Changing the password by itself must not be considered a config change that should trigger a keys reload.
*/
private boolean isKeysConfigChanged(String oldKeyImportFile, Long oldKeyTokenExpiration) {
return ((oldKeyImportFile.equals(keyImportFile) == false) || (oldKeyTokenExpiration != keyTokenExpiration));
private boolean isKeysConfigChanged(String oldKeyImportFile, Long oldKeyTokenExpiration, Long oldExpirationDifferenceAllowed) {
return ((oldKeyImportFile.equals(keyImportFile) == false) || (oldKeyTokenExpiration != keyTokenExpiration) || (oldExpirationDifferenceAllowed != expirationDifferenceAllowed));
}

private boolean isMonitorIntervalChanged(Long oldMonitorInterval) {
Expand Down Expand Up @@ -378,6 +384,12 @@ public String getAuthFilterRef() {
return authFilterRef;
}

/** {@inheritDoc} */
@Override
public long getExpirationDifferenceAllowed() {
return expirationDifferenceAllowed;
}

/**
* Callback method for use by the LTPAKeyCreator class.
* <p>
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2020 IBM Corporation and others.
* Copyright (c) 2012, 2022 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -62,12 +62,14 @@ private Map<String, Object> createTokenFactoryMap() {
LTPAPrivateKey ltpaPrivateKey = new LTPAPrivateKey(keyInfoManager.getPrivateKey(config.getKeyFile()));
LTPAPublicKey ltpaPublicKey = new LTPAPublicKey(keyInfoManager.getPublicKey(config.getKeyFile()));
byte[] sharedKey = keyInfoManager.getSecretKey(config.getKeyFile());
long expDiffAllowed = config.getExpirationDifferenceAllowed();

Map<String, Object> tokenFactoryMap = new HashMap<String, Object>();
tokenFactoryMap.put(LTPAConstants.EXPIRATION, config.getTokenExpiration());
tokenFactoryMap.put(LTPAConstants.SECRET_KEY, sharedKey);
tokenFactoryMap.put(LTPAConstants.PUBLIC_KEY, ltpaPublicKey);
tokenFactoryMap.put(LTPAConstants.PRIVATE_KEY, ltpaPrivateKey);
tokenFactoryMap.put(LTPAConfigurationImpl.KEY_EXP_DIFF_ALLOWED, expDiffAllowed);
return tokenFactoryMap;
}

Expand Down
Expand Up @@ -61,6 +61,7 @@ public class LTPAToken2 implements Token, Serializable {
private final LTPAPublicKey publicKey;
private String cipher = null;
private static final String IBMJCE_NAME = "IBMJCE";
private long expirationDifferenceAllowed;

static {
MessageDigest m1 = null, m2 = null;
Expand Down Expand Up @@ -91,7 +92,7 @@ public class LTPAToken2 implements Token, Serializable {
* @param privateKey The LTPA private key
* @param publicKey The LTPA public key
*/
public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey privateKey, LTPAPublicKey publicKey) throws InvalidTokenException {
public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey privateKey, LTPAPublicKey publicKey, long expDiffAllowed) throws InvalidTokenException {
checkTokenBytes(tokenBytes);
this.signature = null;
this.encryptedBytes = tokenBytes.clone();
Expand All @@ -100,6 +101,7 @@ public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey
this.publicKey = publicKey;
this.expirationInMilliseconds = 0;
this.cipher = AES_CBC_CIPHER;
this.expirationDifferenceAllowed = expDiffAllowed;
decrypt();
}

Expand All @@ -112,7 +114,7 @@ public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey
* @param publicKey The LTPA public key
* @param attributes The list of attributes will be removed from the LTPA2 token
*/
public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey privateKey, LTPAPublicKey publicKey,
public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey privateKey, LTPAPublicKey publicKey, long expDiffAllowed,
String... attributes) throws InvalidTokenException, TokenExpiredException {
checkTokenBytes(tokenBytes);
this.signature = null;
Expand All @@ -122,6 +124,7 @@ public LTPAToken2(byte[] tokenBytes, @Sensitive byte[] sharedKey, LTPAPrivateKey
this.publicKey = publicKey;
this.expirationInMilliseconds = 0;
this.cipher = AES_CBC_CIPHER;
this.expirationDifferenceAllowed = expDiffAllowed;
decrypt();

isValid();
Expand Down Expand Up @@ -232,12 +235,31 @@ private final void decrypt() throws InvalidTokenException {
if (expirationArray != null && expirationArray[expirationArray.length - 1] != null) {
// the new expiration value inside the signature for LTPAToken2
expirationInMilliseconds = Long.parseLong(expirationArray[expirationArray.length - 1]);

// Liberty and Traditional WebSphere both create LTPA Tokens with 3 fields. (userData % expiration % sign)
// Normally, the expiration value is read from the first field of the token, the userData field.
// The expiration value may be read from the second field of the token instead, to maintain legacy support in Traditional WebSphere.

// If the LTPAToken contains both expiration formats, Compare the values to ensure they are within the expiration difference allowed.
// If the difference between the two expiration values is greater than expirationDifferenceAllowed, then an InvalidTokenException will be thrown.
// If expirationDifferenceAllowed is 0, then the two expiration values must match.
// If expirationDifferenceAllowed is less than 0, then the two expiration values are not compared.
// expirationDifferenceAllowed is 3 seconds (3000ms) by default.
if (fields.length == 3 && expirationDifferenceAllowed >= 0 && (Math.abs(expirationInMilliseconds - Long.parseLong(fields[1])) > expirationDifferenceAllowed)) {
if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
Tr.debug(this, tc, "Token validation failed due to the expiration fields having a difference greater than: "
+ expirationDifferenceAllowed + " milliseconds\n"
+ "first field expiration: " + expirationInMilliseconds + " milliseconds\n"
+ "second field expiration: " + fields[1] + " milliseconds");
}
throw new InvalidTokenException("Token Validation Failed");
}
} else {
// the old expiration value outside of the signature for LTPAToken
expirationInMilliseconds = Long.parseLong(fields[1]);
}

byte[] signature = Base64Coder.base64Decode(Base64Coder.getBytes(fields[2]));
// the signature will always be the last field, but the fields array length may be 2 or 3.
byte[] signature = Base64Coder.base64Decode(Base64Coder.getBytes(fields[fields.length-1]));
setSignature(signature);
} catch (BadPaddingException e) {
if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2011, 2019 IBM Corporation and others.
* Copyright (c) 2004, 2022 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -29,6 +29,7 @@ public class LTPAToken2Factory implements TokenFactory {
private byte[] sharedKey;
private LTPAPublicKey publicKey;
private LTPAPrivateKey privateKey;
private long expDiffAllowed;

/** {@inheritDoc} */
@Override
Expand All @@ -37,6 +38,7 @@ public void initialize(@Sensitive Map tokenFactoryMap) {
sharedKey = (byte[]) tokenFactoryMap.get(LTPAConstants.SECRET_KEY);
publicKey = (LTPAPublicKey) tokenFactoryMap.get(LTPAConstants.PUBLIC_KEY);
privateKey = (LTPAPrivateKey) tokenFactoryMap.get(LTPAConstants.PRIVATE_KEY);
expDiffAllowed = (Long) tokenFactoryMap.get(LTPAConfigurationImpl.KEY_EXP_DIFF_ALLOWED);
}

/** {@inheritDoc} */
Expand All @@ -59,13 +61,13 @@ private String getUniqueId(Map tokenData) throws TokenCreationFailedException {
/** {@inheritDoc} */
@Override
public Token validateTokenBytes(byte[] tokenBytes) throws InvalidTokenException, TokenExpiredException {
return new LTPAToken2(tokenBytes, sharedKey, privateKey, publicKey);
return new LTPAToken2(tokenBytes, sharedKey, privateKey, publicKey, expDiffAllowed);
}

/** {@inheritDoc} */
@Override
public Token validateTokenBytes(byte[] tokenBytes, String... removeAttributes) throws InvalidTokenException, TokenExpiredException {
return new LTPAToken2(tokenBytes, sharedKey, privateKey, publicKey, removeAttributes);
return new LTPAToken2(tokenBytes, sharedKey, privateKey, publicKey, expDiffAllowed, removeAttributes);
}

}
Expand Up @@ -79,7 +79,7 @@ public static String createUserData(Map<String, ArrayList<String>> attributes) {
*/
@FFDCIgnore(Exception.class)
protected static final String[] parseToken(String tokenStr) throws InvalidTokenException {
String[] fields = new String[3];
String[] fields = null;
int tokenLen = tokenStr.length();
char c;

Expand Down Expand Up @@ -113,11 +113,15 @@ protected static final String[] parseToken(String tokenStr) throws InvalidTokenE
// Notice: Liberty only supports LTPAToken2
if (expireBegin == -1) {
// Support only LTPAToken2 expire format
// Token format: userData % sign
expireBegin = signBegin;
fields = new String[2];
fields[0] = tokenStr.substring(0, expireBegin - 1);
fields[1] = tokenStr.substring(expireBegin, tokenLen);
} else {
// two DELIM encountered for LTPAToken and LTPAToken2
// Token format: userData % expiration % sign
fields = new String[3];
fields[0] = tokenStr.substring(0, expireBegin - 1);
fields[1] = tokenStr.substring(expireBegin, signBegin - 1);
fields[2] = tokenStr.substring(signBegin, tokenLen);
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012 IBM Corporation and others.
* Copyright (c) 2012, 2022 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -90,7 +90,7 @@ public static void setUpBeforeClass() throws Exception {

@Before
public void setUp() {
props = createProps(PATH_TO_FILE, PWD, 120L, 0L);
props = createProps(PATH_TO_FILE, PWD, 120L, 0L, 0L);

mock.checking(new Expectations() {
{
Expand All @@ -111,12 +111,13 @@ public void setUp() {
ltpaConfig = createActivatedLTPAConfigurationImpl();
}

private Map<String, Object> createProps(String filePath, String password, long expiration, long monitorInterval) {
private Map<String, Object> createProps(String filePath, String password, long expiration, long monitorInterval, long expDiffAllowed) {
Map<String, Object> props = new HashMap<String, Object>();
props.put(LTPAConfiguration.CFG_KEY_IMPORT_FILE, filePath);
props.put(LTPAConfiguration.CFG_KEY_PASSWORD, new SerializableProtectedString(password.toCharArray()));
props.put(LTPAConfiguration.CFG_KEY_TOKEN_EXPIRATION, expiration);
props.put(LTPAConfiguration.CFG_KEY_MONITOR_INTERVAL, monitorInterval);
props.put(LTPAConfigurationImpl.KEY_EXP_DIFF_ALLOWED, expDiffAllowed);
return props;
}

Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2015 IBM Corporation and others.
* Copyright (c) 2012, 2022 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -94,6 +94,7 @@ private void setupProps() {
props.put(LTPAConfiguration.CFG_KEY_PASSWORD, new SerializableProtectedString("notUsed".toCharArray()));
props.put(LTPAConfiguration.CFG_KEY_TOKEN_EXPIRATION, 0L);
props.put(LTPAConfiguration.CFG_KEY_MONITOR_INTERVAL, 0L);
props.put(LTPAConfigurationImpl.KEY_EXP_DIFF_ALLOWED, 0L);
}

private void setupLocationServiceExpecatations() {
Expand Down
Expand Up @@ -75,11 +75,13 @@ private void setupLTPAKeys() throws Exception {

private Map<String, Object> createTestTokenFactoryMap() {
long expectedExpirationLimit = 120;
long expDiffAllowed = 0;
Map<String, Object> tokenFactoryMap = new HashMap<String, Object>();
tokenFactoryMap.put("expiration", expectedExpirationLimit);
tokenFactoryMap.put("ltpa_shared_key", encodedSharedKey.getBytes());
tokenFactoryMap.put("ltpa_public_key", ltpaPublicKey);
tokenFactoryMap.put("ltpa_private_key", ltpaPrivateKey);
tokenFactoryMap.put("expirationDifferenceAllowed", expDiffAllowed);

return tokenFactoryMap;
}
Expand Down

0 comments on commit f9dafab

Please sign in to comment.