Skip to content

Commit

Permalink
Profile Token Enhancements
Browse files Browse the repository at this point in the history
Signed-off-by: John Eberhard <[email protected]>
  • Loading branch information
jeber-ibm committed Dec 3, 2024
1 parent 16dca80 commit cb2a1c8
Show file tree
Hide file tree
Showing 11 changed files with 539 additions and 387 deletions.
61 changes: 53 additions & 8 deletions src/main/java/com/ibm/as400/access/AS400.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.ietf.jgss.GSSManager;

import com.ibm.as400.security.auth.ProfileTokenCredential;
import com.ibm.as400.security.auth.ProfileTokenEnhancedInfo;
import com.ibm.as400.security.auth.ProfileTokenProvider;

/**
Expand Down Expand Up @@ -2706,7 +2707,6 @@ public ProfileTokenCredential getProfileToken(int tokenType, int timeoutInterval
profileToken.setTimeoutInterval(timeoutInterval);
profileToken.setVerificationID(verificationID);
profileToken.setRemoteIPAddress(remoteIPAddress);
profileToken.setAdditionalAuthenticationFactor(additionalAuthenticationFactor_);
}
catch (PropertyVetoException e)
{
Expand All @@ -2726,7 +2726,7 @@ public ProfileTokenCredential getProfileToken(int tokenType, int timeoutInterval

CredentialVault tempVault = (CredentialVault)credVault_.clone();
tempVault.storeEncodedUsingExternalSeeds(proxySeed, impl_.exchangeSeed(proxySeed));
impl_.generateProfileToken(profileToken, userId_, tempVault, gssName_);
impl_.generateProfileToken(profileToken, userId_, tempVault, additionalAuthenticationFactor_, gssName_);
}

return profileToken;
Expand Down Expand Up @@ -2866,7 +2866,7 @@ public ProfileTokenCredential getProfileToken(String userId, String password, in
**/
public ProfileTokenCredential getProfileToken(String userId, char[] password, int tokenType, int timeoutInterval) throws AS400SecurityException, IOException, InterruptedException
{
return getProfileToken(userId, password, null, tokenType, timeoutInterval, null, null);
return getProfileToken(userId, password, null, tokenType, timeoutInterval, null);
}

/**
Expand Down Expand Up @@ -2913,6 +2913,49 @@ public ProfileTokenCredential getProfileToken(String userId, char[] password, in
**/
public ProfileTokenCredential getProfileToken(String userId, char[] password, char[] additionalAuthFactor, int tokenType, int timeoutInterval,
String verificationID, String remoteIPAddress) throws AS400SecurityException, IOException, InterruptedException
{
ProfileTokenEnhancedInfo enhancedInfo = new ProfileTokenEnhancedInfo();
enhancedInfo.setVerificationID(verificationID);
enhancedInfo.setRemoteIPAddress(remoteIPAddress);
return getProfileToken(userId,password,additionalAuthFactor,tokenType, timeoutInterval, enhancedInfo);
}

/**
* Authenticates the given user profile and password and returns a corresponding ProfileTokenCredential if
* successful.
* <p>
* Invoking this method does not change the user ID and password assigned to the system or otherwise modify the user
* or authorities under which the application is running.
* <p>
* This function is only supported if the system is at i5/OS V4R5M0 or greater.
* <p>
* <b>Note:</b> Providing an incorrect password increments the number of failed sign-on attempts for the user
* profile, and can result in the profile being disabled. Refer to documentation on the
* <i>ProfileTokenCredential</i> class for additional restrictions.
*
* @param userId The user profile name.
* @param password The user profile password.
* @param additionalAuthFactor The additional authentication factor or null if not specifying one.
* @param tokenType The type of profile token to create. Possible types are defined as fields on the
* ProfileTokenCredential class:
* <ul>
* <li>{@link com.ibm.as400.security.auth.ProfileTokenCredential#TYPE_SINGLE_USE
* TYPE_SINGLE_USE}
* <li>{@link com.ibm.as400.security.auth.ProfileTokenCredential#TYPE_MULTIPLE_USE_NON_RENEWABLE
* TYPE_MULTIPLE_USE_NON_RENEWABLE}
* <li>{@link com.ibm.as400.security.auth.ProfileTokenCredential#TYPE_MULTIPLE_USE_RENEWABLE
* TYPE_MULTIPLE_USE_RENEWABLE}
* </ul>
* @param timeoutInterval The number of seconds to expiration when the token is created (1-3600).
* @param enhancedInfo Information used for creating an enhanced profile token.
* @return A ProfileTokenCredential representing the authenticated profile and password.
* @exception AS400SecurityException If a security or authority error occurs.
* @exception ExtendedIllegalArgumentException If userId length is not valid.
* @exception IOException If an error occurs while communicating with the system.
* @exception InterruptedException If this thread is interrupted.
**/
public ProfileTokenCredential getProfileToken(String userId, char[] password, char[] additionalAuthFactor, int tokenType, int timeoutInterval,
ProfileTokenEnhancedInfo enhancedInfo ) throws AS400SecurityException, IOException, InterruptedException
{
connectService(AS400.SIGNON);

Expand All @@ -2922,13 +2965,16 @@ public ProfileTokenCredential getProfileToken(String userId, char[] password, ch
if (userId.length() > 10)
throw new ExtendedIllegalArgumentException("userId", ExtendedIllegalArgumentException.LENGTH_NOT_VALID);

if (enhancedInfo == null) {
enhancedInfo = new ProfileTokenEnhancedInfo();
}
checkPasswordNullAndLength(password, "password");

if (isTurkish()) {
userId = userId.toUpperCase(Locale.ENGLISH);
if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "This system locale is Turkish, userId.toUpperCase(Locale.ENGLISH)");
}

userId = resolveUserId(userId.toUpperCase());

ProfileTokenCredential profileToken = new ProfileTokenCredential();
Expand All @@ -2937,9 +2983,8 @@ public ProfileTokenCredential getProfileToken(String userId, char[] password, ch
profileToken.setSystem(this);
profileToken.setTokenType(tokenType);
profileToken.setTimeoutInterval(timeoutInterval);
profileToken.setVerificationID(verificationID);
profileToken.setRemoteIPAddress(remoteIPAddress);
profileToken.setAdditionalAuthenticationFactor(additionalAuthFactor);
profileToken.setEnhancedInfo(enhancedInfo);

}
catch (PropertyVetoException e)
{
Expand All @@ -2953,7 +2998,7 @@ public ProfileTokenCredential getProfileToken(String userId, char[] password, ch
{
PasswordVault tempVault = new PasswordVault(password);
tempVault.storeEncodedUsingExternalSeeds(proxySeed, impl_.exchangeSeed(proxySeed));
impl_.generateProfileToken(profileToken, userId, tempVault, gssName_);
impl_.generateProfileToken(profileToken, userId, tempVault, additionalAuthFactor, gssName_);
}

return profileToken;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/ibm/as400/access/AS400Impl.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface AS400Impl
// Sets the raw bytes for the provided profile token.
void generateProfileToken(ProfileTokenCredential profileToken, String userIdentity) throws AS400SecurityException, IOException;
// Sets the raw bytes for the provided profile token.
void generateProfileToken(ProfileTokenCredential profileToken, String userId, CredentialVault vault, String gssName) throws AS400SecurityException, IOException, InterruptedException;
void generateProfileToken(ProfileTokenCredential profileToken, String userId, CredentialVault vault, char[] additionalAuthenticationFactor, String gssName) throws AS400SecurityException, IOException, InterruptedException;
// Get the port for a service.
int getServicePort(String systemName, int service);
// Check service connection.
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/ibm/as400/access/AS400ImplProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public void generateProfileToken(ProfileTokenCredential profileToken, String use
new boolean[] { true, false }, // indicate that 1st arg gets modified
true);
ProfileTokenCredential returnArg = (ProfileTokenCredential)rv.getArgument(0);
profileToken.setToken(returnArg.getToken(),false);
profileToken.setToken(returnArg.getToken());
return;
}
catch (InvocationTargetException e)
Expand All @@ -156,16 +156,16 @@ public void generateProfileToken(ProfileTokenCredential profileToken, String use

// Sets the raw bytes for the provided profile token.
@Override
public void generateProfileToken(ProfileTokenCredential profileToken, String userId, CredentialVault vault, String gssName) throws AS400SecurityException, IOException, InterruptedException
public void generateProfileToken(ProfileTokenCredential profileToken, String userId, CredentialVault vault, char[] additionalAuthFactor, String gssName) throws AS400SecurityException, IOException, InterruptedException
{
try {
ProxyReturnValue rv = connection_.callMethod (pxId_, "generateProfileToken",
new Class[] { ProfileTokenCredential.class, String.class, CredentialVault.class, String.class },
new Object[] { profileToken, userId, vault, gssName },
new boolean[] { true, false, false, false }, // indicate that 1st arg gets modified
new Class[] { ProfileTokenCredential.class, String.class, CredentialVault.class,char[].class ,String.class },
new Object[] { profileToken, userId, vault, additionalAuthFactor, gssName },
new boolean[] { true, false, false, false, false }, // indicate that 1st arg gets modified
true);
ProfileTokenCredential returnArg = (ProfileTokenCredential)rv.getArgument(0);
profileToken.setToken(returnArg.getToken(),false);
profileToken.setToken(returnArg.getToken());
return;
}
catch (InvocationTargetException e)
Expand Down
99 changes: 72 additions & 27 deletions src/main/java/com/ibm/as400/access/AS400ImplRemote.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.ietf.jgss.GSSCredential;

import com.ibm.as400.security.auth.ProfileTokenCredential;
import com.ibm.as400.security.auth.ProfileTokenEnhancedInfo;

/**
* This is the functional implementation of the AS400Impl interface. While
Expand Down Expand Up @@ -943,13 +944,13 @@ public void generateProfileToken(ProfileTokenCredential profileToken, String use
throw AS400ImplRemote.returnSecurityException(reply.getRC(), null, userId_);
}

// [0]=factor, [1]=verification ID, [2]=remote ip address
Object[] additonalAuthInfo = getAdditionalAuthInfo(profileToken, null);
// [0]=notUsed, [1]=verification ID, [2]=remote ip address
Object[] additionalAuthInfo = getAdditionalAuthInfo(profileToken, null);

SignonGenAuthTokenRequestDS req2 = new SignonGenAuthTokenRequestDS(
BinaryConverter.charArrayToByteArray(userIdentity.toCharArray()), profileToken.getTokenType(),
profileToken.getTimeoutInterval(), serverLevel_,
(byte[])additonalAuthInfo[1], (byte[])additonalAuthInfo[2]);
(byte[])additionalAuthInfo[1], (byte[])additionalAuthInfo[2]);
SignonGenAuthTokenReplyDS rep = (SignonGenAuthTokenReplyDS) signonServer_.sendAndReceive(req2);

int rc = rep.getRC();
Expand All @@ -963,13 +964,22 @@ public void generateProfileToken(ProfileTokenCredential profileToken, String use
userId_);
}
try {
boolean enhancedProfileToken = false;
int vrm = (version_ != null) ? version_.getVersionReleaseModification() : getVRM();
if (vrm > 0x00070500 ) {
enhancedProfileToken = true; /* server always generates enhanced profile token */
/* server always generates enhanced profile token */
ProfileTokenEnhancedInfo enhancedInfo = new ProfileTokenEnhancedInfo((String) additionalAuthInfo[3],
(String) additionalAuthInfo[4],
profileToken.getRemotePort(),
profileToken.getLocalIPAddress(),
profileToken.getLocalPort());
enhancedInfo.setEnhancedTokenCreated(true);
profileToken.setToken(rep.getProfileTokenBytes(),
enhancedInfo);
} else {
profileToken.setToken(rep.getProfileTokenBytes());
}

profileToken.setToken(rep.getProfileTokenBytes(), enhancedProfileToken);

profileToken.setTokenCreator(ProfileTokenCredential.CREATOR_SIGNON_SERVER);
}
catch (PropertyVetoException e) {
Expand All @@ -987,7 +997,7 @@ public void generateProfileToken(ProfileTokenCredential profileToken, String use
}

@Override
public void generateProfileToken(ProfileTokenCredential profileToken, String userId, CredentialVault vault, String gssName)
public void generateProfileToken(ProfileTokenCredential profileToken, String userId, CredentialVault vault, char[] additionalAuthFactor, String gssName)
throws AS400SecurityException, IOException, InterruptedException
{
signonConnect();
Expand Down Expand Up @@ -1128,13 +1138,13 @@ else if (passwordLevel_ < 4)
if (Trace.isTraceOn()) Trace.log(Trace.DIAGNOSTIC, "AS400ImplRemote generating profile token for user: " + userId);


// [0]=factor, [1]=verification ID, [2]=remote ip address
Object[] additonalAuthInfo = getAdditionalAuthInfo(profileToken, null);
// [0]=unused, [1]=verification ID, [2]=remote ip address
Object[] additionalAuthInfo = getAdditionalAuthInfo(profileToken, null);

AS400GenAuthTknDS req = new AS400GenAuthTknDS(userIdEbcdic,
authenticationBytes, authScheme, profileToken.getTokenType(),
profileToken.getTimeoutInterval(), serverLevel_,
(byte[])additonalAuthInfo[0], (byte[])additonalAuthInfo[1], (byte[])additonalAuthInfo[2]);
additionalAuthFactor_, (byte[])additionalAuthInfo[1], (byte[])additionalAuthInfo[2]);

CredentialVault.clearArray(authenticationBytes);
AS400GenAuthTknReplyDS rep = (AS400GenAuthTknReplyDS) signonServer_.sendAndReceive(req);
Expand All @@ -1151,13 +1161,19 @@ else if (passwordLevel_ < 4)
}

try {
boolean enhancedProfileToken = false;
int vrm = (version_ != null) ? version_.getVersionReleaseModification() : getVRM();
if (vrm > 0x00070500 ) {
enhancedProfileToken = true; /* server always generates enhanced profile token */
ProfileTokenEnhancedInfo enhancedInfo = new ProfileTokenEnhancedInfo((String) additionalAuthInfo[3],
(String) additionalAuthInfo[4],
profileToken.getRemotePort(),
profileToken.getLocalIPAddress(),
profileToken.getLocalPort());
enhancedInfo.setEnhancedTokenCreated(true);
profileToken.setToken(rep.getProfileTokenBytes(),
enhancedInfo );
} else {
profileToken.setToken(rep.getProfileTokenBytes());
}

profileToken.setToken(rep.getProfileTokenBytes(), enhancedProfileToken);
profileToken.setTokenCreator(ProfileTokenCredential.CREATOR_SIGNON_SERVER);
} catch (PropertyVetoException e) {
Trace.log(Trace.ERROR, e);
Expand Down Expand Up @@ -5124,7 +5140,15 @@ public void setAdditionalAuthenticationFactor(char[] additionalAuthFactor)
additionalAuthFactor_ = (null != additionalAuthFactor && 0 < additionalAuthFactor.length )
? (new String(additionalAuthFactor)).getBytes(StandardCharsets.UTF_8) : null;
}

/*
* Get the additional authentication info to be sent to the server as well as
* the information that needs to be associated with an enhanced profile token.
* The output is the following
* object[1] = byte[] verification id in UTF-8
* object[2] = byte[] remoteIPAddress in UTF-8
* object[3] = String verificationId
* object[4] = String remoteIpAddress
*/
private Object[] getAdditionalAuthInfo(ProfileTokenCredential profileToken, Boolean aafIndicator)
{
Object[] authdata = new Object[] { null, null, null };
Expand All @@ -5139,16 +5163,27 @@ private Object[] getAdditionalAuthInfo(ProfileTokenCredential profileToken, Bool
if (profileToken == null && (credVault_ instanceof ProfileTokenVault))
profileToken = ((ProfileTokenVault)credVault_).getProfileTokenCredential();

if (profileToken != null)
{
String verificationID_s = profileToken.getVerificationID();
authdata[1] = (verificationID_s != null && !verificationID_s.isEmpty()) ? verificationID_s.getBytes(StandardCharsets.UTF_8) : null;

String clientIPAddress_s = profileToken.getRemoteIPAddress();
authdata[2] = (clientIPAddress_s != null && !clientIPAddress_s.isEmpty()) ? clientIPAddress_s.getBytes(StandardCharsets.UTF_8) : null;

// Verification ID will always be set to something. However, depending on where token was created, the client IP address
// may or may not be set. If creating the token and it is not set, use the IP address of the sign-on server. Thus, all tokens
if (profileToken != null) {
String verificationID_s = profileToken.getVerificationID();
if (verificationID_s == null || verificationID_s.isEmpty() ) {
/* Make sure a default value is set */
verificationID_s = ProfileTokenCredential.DEFAULT_VERIFICATION_ID;
}
authdata[1] = verificationID_s.getBytes(StandardCharsets.UTF_8);
authdata[3] = verificationID_s;

String clientIPAddress_s = profileToken.getRemoteIPAddress();
if (clientIPAddress_s != null && !clientIPAddress_s.isEmpty()) {
authdata[2] = clientIPAddress_s.getBytes(StandardCharsets.UTF_8);
authdata[4] = clientIPAddress_s;
} else {
authdata[2] = null;
authdata[4] = null;
}

// Verification ID will always be set to something. However, depending on where
// token was created, the client IP address
// may or may not be set. If creating the token and it is not set, use the IP address of the sign-on server. Thus, all tokens
// that signon server creates should have client IP address.
if (authdata[2] == null)
{
Expand All @@ -5157,15 +5192,25 @@ private Object[] getAdditionalAuthInfo(ProfileTokenCredential profileToken, Bool
{
// We are creating token, try to set client IP address using sign-on server.
clientIPAddress_s = signonServer_.getLocalAddress();
authdata[2] = (clientIPAddress_s != null && !clientIPAddress_s.isEmpty()) ? clientIPAddress_s.getBytes(StandardCharsets.UTF_8) : null;
if (clientIPAddress_s != null && !clientIPAddress_s.isEmpty()) {
authdata[2] = clientIPAddress_s.getBytes(StandardCharsets.UTF_8);
authdata[4] = clientIPAddress_s;
} else {
authdata[2] = null;
authdata[4] = null;
}

try {
profileToken.setRemoteIPAddress(clientIPAddress_s);
} catch (PropertyVetoException e) {
throw new InternalErrorException(InternalErrorException.UNEXPECTED_EXCEPTION, e);
}
}
else if (profileToken.getTokenCreator() != ProfileTokenCredential.CREATOR_SIGNON_SERVER)
else if (profileToken.getTokenCreator() != ProfileTokenCredential.CREATOR_SIGNON_SERVER) {

authdata[2] = "*NOUSE".getBytes(StandardCharsets.UTF_8);
authdata[4] = "*NOUSE";
}
}
}
}
Expand Down
Loading

0 comments on commit cb2a1c8

Please sign in to comment.