diff --git a/src/main/java/com/ibm/as400/access/AS400.java b/src/main/java/com/ibm/as400/access/AS400.java index 975d3ec4..ddb7f1a2 100644 --- a/src/main/java/com/ibm/as400/access/AS400.java +++ b/src/main/java/com/ibm/as400/access/AS400.java @@ -26,6 +26,7 @@ import java.net.UnknownHostException; import java.net.URL; import java.util.Arrays; +import java.util.Base64; import java.util.GregorianCalendar; import java.util.Hashtable; import java.util.Locale; @@ -388,6 +389,48 @@ public class AS400 implements Serializable, AutoCloseable private boolean forcePrompt_ = false; private int validateSignonTimeOut_ = 0; + private transient CredentialVault kerbTicket_; + + // Prefix used to indicate that the password contains a base64-encoded Kerberos token. + public static final String KERBEROS_PREFIX = "_KERBEROSAUTH_"; + public static final char[] KERBEROS_PREFIX_CHARS = KERBEROS_PREFIX.toCharArray(); + + private void setKerbTicket(byte[] ticket) { + this.kerbTicket_ = new PasswordVault(ticket); + } + + public void clearKerbTicket() { + if (!this.kerbTicket_.isEmpty()) + this.kerbTicket_.empty(); + } + + // Determines if the password contains a Kerberos token + private boolean isKerbTicket(char[] auth){ + char[] prefix = KERBEROS_PREFIX_CHARS; + if (auth == null || auth.length < prefix.length) { + return false; + } + + for (int i = 0; i < prefix.length; i++) { + if (auth[i] != prefix[i]) { + return false; + } + } + return true; + } + + // Extracts the Kerberos token from the password + private static char[] getKerbTicketFromPassword(char[] password) { + int prefixLen = KERBEROS_PREFIX_CHARS.length; + int tokenLen = password.length - prefixLen; + + char[] tokenChars = new char[tokenLen]; + System.arraycopy(password, prefixLen, tokenChars, 0, tokenLen); + + return tokenChars; + } + + /** * Constructs an AS400 object. *
@@ -647,7 +690,6 @@ public AS400(String systemName, String userId, char[] password) if (userId.length() > 10) throw new ExtendedIllegalArgumentException("userId (" + userId + ")", ExtendedIllegalArgumentException.LENGTH_NOT_VALID); - checkPasswordNullAndLength(password, "password"); construct(); systemName_ = systemName; systemNameLocal_ = resolveSystemNameLocal(systemName); @@ -658,7 +700,17 @@ public AS400(String systemName, String userId, char[] password) } userId_ = userId.toUpperCase(); - credVault_ = new PasswordVault(password); + // Create appropriate credential vault based on whether the password is a Kerberos token or a regular password. + boolean isKerberosTicket = isKerbTicket(password); + if (isKerberosTicket){ + password = getKerbTicketFromPassword(password); + credVault_ = new GSSTokenVault(); + + this.setKerbTicket(Base64.getDecoder().decode((new String(password)))); + }else{ + checkPasswordNullAndLength(password, "password"); + credVault_ = new PasswordVault(password); + } proxyServer_ = resolveProxyServer(proxyServer_); } @@ -1794,6 +1846,10 @@ private synchronized void chooseImpl() } } + // If kerbTicket_ has been set, make sure the impl knows about it. + if (!kerbTicket_.isEmpty()) + impl_.setKerbTicket(kerbTicket_.getClearCredential()); + if (!propertiesFrozen_) { impl_.setState(useSSLConnection_, canUseNativeOptimizations(), threadUsed_, ccsid_, nlv_, @@ -4145,6 +4201,7 @@ public void removeVetoableChangeListener(VetoableChangeListener listener) public synchronized void resetAllServices() { if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Resetting all services."); + clearKerbTicket(); setStayAlive(0); disconnectAllServices(); @@ -5443,9 +5500,17 @@ synchronized void signon(boolean keepConnection) throws AS400SecurityException, && (credVault_.getType() == AUTHENTICATION_SCHEME_GSS_TOKEN || gssOption_ != AS400.GSS_OPTION_NONE)) { // Try for Kerberos. - byte[] newBytes = (gssCredential_ == null) ? TokenManager.getGSSToken(systemName_, gssName_) : - TokenManager2.getGSSToken(systemName_, gssCredential_); - + byte[] newBytes = null; + + if (!kerbTicket_.isEmpty() && kerbTicket_.getClearCredential().length > 0) { + if (Trace.traceOn_) Trace.log(Trace.DIAGNOSTIC, "Using injected Kerberos ticket."); + newBytes = kerbTicket_.getClearCredential(); + } else { + // Fall back to generating the token normally + newBytes = (gssCredential_ == null) + ? TokenManager.getGSSToken(systemName_, gssName_) + : TokenManager2.getGSSToken(systemName_, gssCredential_); + } // We do not have to empty the existing vault because the // previous if-check assures us it is already empty. credVault_ = new GSSTokenVault(newBytes); diff --git a/src/main/java/com/ibm/as400/access/AS400Impl.java b/src/main/java/com/ibm/as400/access/AS400Impl.java index 8528961e..c0d5fab5 100644 --- a/src/main/java/com/ibm/as400/access/AS400Impl.java +++ b/src/main/java/com/ibm/as400/access/AS400Impl.java @@ -94,4 +94,7 @@ interface AS400Impl String getSystemName(); /* Set the VRM for the object. Only set for the remote Impl */ void setVRM(int v, int r, int m); + + + void setKerbTicket(byte[] ticket); } diff --git a/src/main/java/com/ibm/as400/access/AS400ImplProxy.java b/src/main/java/com/ibm/as400/access/AS400ImplProxy.java index f68ec4e1..5ab8ff9a 100644 --- a/src/main/java/com/ibm/as400/access/AS400ImplProxy.java +++ b/src/main/java/com/ibm/as400/access/AS400ImplProxy.java @@ -371,6 +371,7 @@ public SignonInfo skipSignon(String systemName, boolean systemNameLocal, String private int bidiStringType = BidiStringType.DEFAULT; + private CredentialVault kerbTicket_; /** * Sets bidi string type of the connection. @@ -404,4 +405,10 @@ public void setVRM(int v, int r, int m) { public void setAdditionalAuthenticationFactor(char[] additionalAuthFactor) { // Does nothing for the proxy class } + + @Override + public void setKerbTicket(byte[] ticket) { + this.kerbTicket_ = new PasswordVault(ticket); + } + } diff --git a/src/main/java/com/ibm/as400/access/AS400ImplRemote.java b/src/main/java/com/ibm/as400/access/AS400ImplRemote.java index 615f583e..d437a938 100644 --- a/src/main/java/com/ibm/as400/access/AS400ImplRemote.java +++ b/src/main/java/com/ibm/as400/access/AS400ImplRemote.java @@ -188,6 +188,9 @@ public class AS400ImplRemote implements AS400Impl private static final String CLASSNAME = "com.ibm.as400.access.AS400ImplRemote"; + // GSS Token, for Kerberos. + private CredentialVault kerbTicket_; + static { if (Trace.traceOn_) Trace.logLoadPath(CLASSNAME); @@ -665,10 +668,14 @@ public int createUserHandle() throws AS400SecurityException, IOException { try { - byte[] authenticationBytes = (gssCredential_ == null) + byte[] authenticationBytes; + if (!this.kerbTicket_.isEmpty()){ + authenticationBytes = this.kerbTicket_.getClearCredential(); + } else { + authenticationBytes = (gssCredential_ == null) ? TokenManager.getGSSToken(systemName_, gssName_) : TokenManager2.getGSSToken(systemName_, gssCredential_); - + } IFSUserHandle2Req req = new IFSUserHandle2Req(authenticationBytes, aafIndicator_ ? additionalAuthFactor_ : null); ds = (ClientAccessDataStream) connectedServer.sendAndReceive(req); } @@ -1016,9 +1023,13 @@ public void generateProfileToken(ProfileTokenCredential profileToken, String use case AS400.AUTHENTICATION_SCHEME_GSS_TOKEN: try { - authenticationBytes = (gssCredential_ == null) - ? TokenManager.getGSSToken(systemName_, gssName) - : TokenManager2.getGSSToken(systemName_, gssCredential_); + if (!this.kerbTicket_.isEmpty()){ + authenticationBytes = this.kerbTicket_.getClearCredential(); + } else { + authenticationBytes = (gssCredential_ == null) + ? TokenManager.getGSSToken(systemName_, gssName) + : TokenManager2.getGSSToken(systemName_, gssCredential_); + } } catch (Exception e) { @@ -1838,6 +1849,8 @@ byte[] getPassword(byte[] clientSeed, byte[] serverSeed) throws AS400SecurityExc if (credType == AS400.AUTHENTICATION_SCHEME_GSS_TOKEN) { try { + if (!kerbTicket_.isEmpty()) + return kerbTicket_.getClearCredential(); return (gssCredential_ == null) ? TokenManager.getGSSToken(systemName_, gssName_) : TokenManager2.getGSSToken(systemName_, gssCredential_); @@ -2216,6 +2229,8 @@ private byte[] getDdmEncryptedPassword(byte[] sharedPrivateKey, byte[] serverSee if (credType == AS400.AUTHENTICATION_SCHEME_GSS_TOKEN) { try { + if (!kerbTicket_.isEmpty()) + return kerbTicket_.getClearCredential(); return (gssCredential_ == null) ? TokenManager.getGSSToken(systemName_, gssName_) : TokenManager2.getGSSToken(systemName_, gssCredential_); @@ -5402,4 +5417,10 @@ else if (profileToken.getTokenCreator() != ProfileTokenCredential.CREATOR_SIGNON public String getLocalIPAddress() { return localIPAddress_; } + + @Override + public void setKerbTicket(byte[] ticket) { + this.kerbTicket_ = new PasswordVault(ticket); + } + } \ No newline at end of file diff --git a/src/main/java/com/ibm/as400/access/AS400JDBCConnectionImpl.java b/src/main/java/com/ibm/as400/access/AS400JDBCConnectionImpl.java index aae8bcc8..eeffb5d5 100644 --- a/src/main/java/com/ibm/as400/access/AS400JDBCConnectionImpl.java +++ b/src/main/java/com/ibm/as400/access/AS400JDBCConnectionImpl.java @@ -592,6 +592,7 @@ public void close () as400_.disconnectServer (server_); + server_ = null; }