From 13b8d313ba9f91882899c1a63ce47f89d692d290 Mon Sep 17 00:00:00 2001 From: Jay Kline Date: Wed, 14 Nov 2007 16:27:47 +0000 Subject: [PATCH] Big change for authentication, which now supports more SASL mechanisms and callbacks. This should address issues SMACK-210 and SMACK-142, as well as set the stage for SMACK-234. git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@9498 b35dd754-fafc-0310-a699-88a17e54d16e --- .../smack/ConnectionConfiguration.java | 68 ++++++- .../smack/NonSASLAuthentication.java | 16 ++ .../smack/SASLAuthentication.java | 184 ++++++++++++++---- .../smack/SmackConfiguration.java | 61 ++++++ .../smack/UserAuthentication.java | 24 +++ .../jivesoftware/smack/XMPPConnection.java | 165 +++++++++++++++- .../smack/sasl/SASLAnonymous.java | 44 +++-- .../smack/sasl/SASLCramMD5Mechanism.java | 38 ++++ .../smack/sasl/SASLDigestMD5Mechanism.java | 38 ++++ .../smack/sasl/SASLExternalMechanism.java | 59 ++++++ .../smack/sasl/SASLGSSAPIMechanism.java | 101 +++------- .../smack/sasl/SASLMechanism.java | 160 +++++++++++---- .../smack/sasl/SASLPlainMechanism.java | 26 +-- test/org/jivesoftware/smack/LoginTest.java | 6 +- 14 files changed, 801 insertions(+), 189 deletions(-) create mode 100644 source/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java create mode 100644 source/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java create mode 100644 source/org/jivesoftware/smack/sasl/SASLExternalMechanism.java diff --git a/source/org/jivesoftware/smack/ConnectionConfiguration.java b/source/org/jivesoftware/smack/ConnectionConfiguration.java index 2a7122fa1..3321e4edf 100644 --- a/source/org/jivesoftware/smack/ConnectionConfiguration.java +++ b/source/org/jivesoftware/smack/ConnectionConfiguration.java @@ -44,6 +44,9 @@ public class ConnectionConfiguration implements Cloneable { private String truststorePath; private String truststoreType; private String truststorePassword; + private String keystorePath; + private String keystoreType; + private String keystorePassword; private boolean verifyChainEnabled = false; private boolean verifyRootCAEnabled = false; private boolean selfSignedCertificateEnabled = false; @@ -128,6 +131,9 @@ public class ConnectionConfiguration implements Cloneable { truststoreType = "jks"; // Set the default password of the cacert file that is "changeit" truststorePassword = "changeit"; + keystorePath = System.getProperty("javax.net.ssl.keyStore"); + keystoreType = "jks"; + keystorePassword = "changeit"; } /** @@ -240,6 +246,66 @@ public class ConnectionConfiguration implements Cloneable { this.truststorePassword = truststorePassword; } + /** + * Retuns the path to the keystore file. The key store file contains the + * certificates that may be used to authenticate the client to the server, + * in the event the server requests or requires it. + * + * @return the path to the keystore file. + */ + public String getKeystorePath() { + return keystorePath; + } + + /** + * Sets the path to the keystore file. The key store file contains the + * certificates that may be used to authenticate the client to the server, + * in the event the server requests or requires it. + * + * @param keystorePath the path to the keystore file. + */ + public void setKeystorePath(String keystorePath) { + this.keystorePath = keystorePath; + } + + /** + * Returns the keystore type, or null if it's not set. + * + * @return the keystore type. + */ + public String getKeystoreType() { + return keystoreType; + } + + /** + * Sets the keystore type. + * + * @param keystoreType the keystore type. + */ + public void setKeystoreType(String keystoreType) { + this.keystoreType = keystoreType; + } + + /** + * Returns the password to use to access the keystore file. It is assumed that all + * certificates share the same password in the keystore. + * + * @return the password to use to access the keystore file. + */ + public String getKeystorePassword() { + return keystorePassword; + } + + /** + * Sets the password to use to access the keystore file. It is assumed that all + * certificates share the same password in the trust store. + * + * @param keystorePassword the password to use to access the keystore file. + */ + public void setKeystorePassword(String keystorePassword) { + this.keystorePassword = keystorePassword; + } + /** * Returns true if the whole chain of certificates presented by the server are going to * be checked. By default the certificate chain is not verified. @@ -520,4 +586,4 @@ public class ConnectionConfiguration implements Cloneable { this.resource = resource; this.sendPresence = sendPresence; } -} \ No newline at end of file +} diff --git a/source/org/jivesoftware/smack/NonSASLAuthentication.java b/source/org/jivesoftware/smack/NonSASLAuthentication.java index 4eef6f7a8..65a584e06 100644 --- a/source/org/jivesoftware/smack/NonSASLAuthentication.java +++ b/source/org/jivesoftware/smack/NonSASLAuthentication.java @@ -24,6 +24,11 @@ import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.packet.Authentication; import org.jivesoftware.smack.packet.IQ; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.Callback; +import java.io.IOException; + /** * Implementation of JEP-0078: Non-SASL Authentication. Follow the following * link to obtain more @@ -40,6 +45,17 @@ class NonSASLAuthentication implements UserAuthentication { this.connection = connection; } + public String authenticate(String username, String resource, CallbackHandler cbh) throws XMPPException { + //Use the callback handler to determine the password, and continue on. + PasswordCallback pcb = new PasswordCallback("Password: ",false); + try { + cbh.handle(new Callback[]{pcb}); + return authenticate(username, String.valueOf(pcb.getPassword()),resource); + } catch (Exception e) { + throw new XMPPException("Unable to determine password.",e); + } + } + public String authenticate(String username, String password, String resource) throws XMPPException { // If we send an authentication packet in "get" mode with just the username, diff --git a/source/org/jivesoftware/smack/SASLAuthentication.java b/source/org/jivesoftware/smack/SASLAuthentication.java index 45eedd8cf..22f0f030e 100644 --- a/source/org/jivesoftware/smack/SASLAuthentication.java +++ b/source/org/jivesoftware/smack/SASLAuthentication.java @@ -24,39 +24,49 @@ import org.jivesoftware.smack.filter.PacketIDFilter; import org.jivesoftware.smack.packet.Bind; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Session; -import org.jivesoftware.smack.sasl.SASLAnonymous; import org.jivesoftware.smack.sasl.SASLMechanism; +import org.jivesoftware.smack.sasl.SASLAnonymous; import org.jivesoftware.smack.sasl.SASLPlainMechanism; +import org.jivesoftware.smack.sasl.SASLCramMD5Mechanism; +import org.jivesoftware.smack.sasl.SASLDigestMD5Mechanism; +import org.jivesoftware.smack.sasl.SASLExternalMechanism; import org.jivesoftware.smack.sasl.SASLGSSAPIMechanism; import java.io.IOException; import java.lang.reflect.Constructor; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; import java.util.*; /** - * This class is responsible authenticating the user using SASL, binding the resource - * to the connection and establishing a session with the server.

+ *

This class is responsible authenticating the user using SASL, binding the resource + * to the connection and establishing a session with the server.

* - * Once TLS has been negotiated (i.e. the connection has been secured) it is possible to + *

Once TLS has been negotiated (i.e. the connection has been secured) it is possible to * register with the server, authenticate using Non-SASL or authenticate using SASL. If the * server supports SASL then Smack will first try to authenticate using SASL. But if that - * fails then Non-SASL will be tried.

+ * fails then Non-SASL will be tried.

* - * The server may support many SASL mechanisms to use for authenticating. Out of the box - * Smack provides SASL PLAIN but it is possible to register new SASL Mechanisms. Use - * {@link #registerSASLMechanism(int, String, Class)} to add new mechanisms. See - * {@link SASLMechanism}.

+ *

The server may support many SASL mechanisms to use for authenticating. Out of the box + * Smack provides several SASL mechanisms, but it is possible to register new SASL Mechanisms. Use + * {@link #registerSASLMechanism(String, Class)} to register a new mechanisms. A registered + * mechanism wont be used until {@link #supportSASLMechanism(String, int)} is called. By default, + * the list of supported SASL mechanisms is determined from the {@link SmackConfiguration}.

* - * Once the user has been authenticated with SASL, it is necessary to bind a resource for + *

Once the user has been authenticated with SASL, it is necessary to bind a resource for * the connection. If no resource is passed in {@link #authenticate(String, String, String)} * then the server will assign a resource for the connection. In case a resource is passed * then the server will receive the desired resource but may assign a modified resource for - * the connection.

+ * the connection.

* - * Once a resource has been binded and if the server supports sessions then Smack will establish - * a session so that instant messaging and presence functionalities may be used. + *

Once a resource has been binded and if the server supports sessions then Smack will establish + * a session so that instant messaging and presence functionalities may be used.

+ * + * @see org.jivesoftware.smack.sasl.SASLMechanism * * @author Gaston Dombiak + * @author Jay Kline */ public class SASLAuthentication implements UserAuthentication { @@ -79,31 +89,37 @@ public class SASLAuthentication implements UserAuthentication { private boolean sessionSupported; static { + // Register SASL mechanisms supported by Smack - registerSASLMechanism(0, "GSSAPI", SASLGSSAPIMechanism.class); - registerSASLMechanism(1, "PLAIN", SASLPlainMechanism.class); + registerSASLMechanism("EXTERNAL", SASLExternalMechanism.class); + registerSASLMechanism("GSSAPI", SASLGSSAPIMechanism.class); + registerSASLMechanism("DIGEST-MD5", SASLDigestMD5Mechanism.class); + registerSASLMechanism("CRAM-MD5", SASLCramMD5Mechanism.class); + registerSASLMechanism("PLAIN", SASLPlainMechanism.class); + registerSASLMechanism("ANONYMOUS", SASLAnonymous.class); + + supportSASLMechanism("GSSAPI",0); + supportSASLMechanism("DIGEST-MD5",1); + supportSASLMechanism("CRAM-MD5",2); + supportSASLMechanism("PLAIN",3); + supportSASLMechanism("ANONYMOUS",4); + } /** - * Registers a new SASL mechanism in the specified preference position. The client will try - * to authenticate using the most prefered SASL mechanism that is also supported by the server. - *

- *

- * Use the index parameter to set the level of preference of the new SASL mechanism. - * A value of 0 means that the mechanism is the most prefered one. + * Registers a new SASL mechanism * - * @param index preference position amongst all the implemented SASL mechanism. Starts with 0. * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4. * @param mClass a SASLMechanism subclass. */ - public static void registerSASLMechanism(int index, String name, Class mClass) { + public static void registerSASLMechanism(String name, Class mClass) { implementedMechanisms.put(name, mClass); - mechanismsPreferences.add(index, name); } /** * Unregisters an existing SASL mechanism. Once the mechanism has been unregistered it won't - * be possible to authenticate users using the removed SASL mechanism. + * be possible to authenticate users using the removed SASL mechanism. It also removes the + * mechanism from the supported list. * * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4. */ @@ -112,6 +128,43 @@ public class SASLAuthentication implements UserAuthentication { mechanismsPreferences.remove(name); } + + /** Registers a new SASL mechanism in the specified preference position. The client will try + * to authenticate using the most prefered SASL mechanism that is also supported by the server. + * The SASL mechanism must be registered via {@link #registerSASLMechanism(String, Class) + * + * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4. + */ + + public static void supportSASLMechanism(String name) { + mechanismsPreferences.add(0, name); + } + + /** Registers a new SASL mechanism in the specified preference position. The client will try + * to authenticate using the most prefered SASL mechanism that is also supported by the server. + * Use the index parameter to set the level of preference of the new SASL mechanism. + * A value of 0 means that the mechanism is the most prefered one. The SASL mechanism must be + * registered via {@link #registerSASLMechanism(String, Class) + * + * @param index preference position amongst all the implemented SASL mechanism. Starts with 0. + * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4. + */ + + public static void supportSASLMechanism(String name, int index) { + mechanismsPreferences.add(index, name); + } + + /** + * Un-supports an existing SASL mechanism. Once the mechanism has been unregistered it won't + * be possible to authenticate users using the removed SASL mechanism. Note that the mechanism + * is still registered, but will just not be used. + * + * @param name common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4. + */ + public static void unsupportSASLMechanism(String name) { + mechanismsPreferences.remove(name); + } + /** * Returns the registerd SASLMechanism classes sorted by the level of preference. * @@ -146,14 +199,79 @@ public class SASLAuthentication implements UserAuthentication { * @return true if the server offered SASL authentication besides ANONYMOUS SASL. */ public boolean hasNonAnonymousAuthentication() { - if (!serverMechanisms.isEmpty()) { - // Check that anonymous sasl is not the only supported mechanism - if (serverMechanisms.size() == 1) { - return !hasAnonymousAuthentication(); + return !serverMechanisms.isEmpty() && (serverMechanisms.size() != 1 || !hasAnonymousAuthentication()); + } + + /** + * Performs SASL authentication of the specified user. If SASL authentication was successful + * then resource binding and session establishment will be performed. This method will return + * the full JID provided by the server while binding a resource to the connection.

+ * + * The server may assign a full JID with a username or resource different than the requested + * by this method. + * + * @param username the username that is authenticating with the server. + * @param resource the desired resource. + * @param cbh the CallbackHandler used to get information from the user + * @return the full JID provided by the server while binding a resource to the connection. + * @throws XMPPException if an error occures while authenticating. + */ + public String authenticate(String username, String resource, CallbackHandler cbh) + throws XMPPException { + // Locate the SASLMechanism to use + Class selected = null; + for (String mechanism : mechanismsPreferences) { + if (implementedMechanisms.containsKey(mechanism) && + serverMechanisms.contains(mechanism)) { + selected = implementedMechanisms.get(mechanism); + break; } - return true; } - return false; + if (selected != null) { + // A SASL mechanism was found. Authenticate using the selected mechanism and then + // proceed to bind a resource + try { + Constructor constructor = selected + .getConstructor(new Class[]{SASLAuthentication.class}); + currentMechanism = (SASLMechanism) constructor.newInstance(this); + // Trigger SASL authentication with the selected mechanism. We use + // connection.getHost() since GSAPI requires the FQDN of the server, which + // may not match the XMPP domain. + currentMechanism.authenticate(username, connection.getHost(), cbh); + + // Wait until SASL negotiation finishes + synchronized (this) { + if (!saslNegotiated && !saslFailed) { + try { + wait(30000); + } catch (InterruptedException e) { + } + } + } + + if (saslFailed) { + // SASL authentication failed and the server may have closed the connection + // so throw an exception + throw new XMPPException("SASL authentication failed"); + } + + if (saslNegotiated) { + // Bind a resource for this connection and + return bindResourceAndEstablishSession(resource); + } else { + // SASL authentication failed + } + } + catch (XMPPException e) { + throw e; + } + catch (Exception e) { + e.printStackTrace(); + } + } else { + throw new XMPPException("SASL Authentication failed. No known authentication mechanisims."); + } + throw new XMPPException("SASL authentication failed"); } /** @@ -187,7 +305,7 @@ public class SASLAuthentication implements UserAuthentication { try { Constructor constructor = selected .getConstructor(new Class[]{SASLAuthentication.class}); - currentMechanism = (SASLMechanism) constructor.newInstance(new Object[]{this}); + currentMechanism = (SASLMechanism) constructor.newInstance(this); // Trigger SASL authentication with the selected mechanism. We use // connection.getHost() since GSAPI requires the FQDN of the server, which // may not match the XMPP domain. @@ -247,7 +365,7 @@ public class SASLAuthentication implements UserAuthentication { public String authenticateAnonymously() throws XMPPException { try { currentMechanism = new SASLAnonymous(this); - currentMechanism.authenticate(null, null, null); + currentMechanism.authenticate(null,null,""); // Wait until SASL negotiation finishes synchronized (this) { diff --git a/source/org/jivesoftware/smack/SmackConfiguration.java b/source/org/jivesoftware/smack/SmackConfiguration.java index c42e02459..0e344dd5f 100644 --- a/source/org/jivesoftware/smack/SmackConfiguration.java +++ b/source/org/jivesoftware/smack/SmackConfiguration.java @@ -26,6 +26,8 @@ import org.xmlpull.v1.XmlPullParser; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; +import java.util.Vector; +import java.util.Collection; import java.util.Enumeration; import java.util.List; @@ -50,6 +52,7 @@ public final class SmackConfiguration { private static int packetReplyTimeout = 5000; private static int keepAliveInterval = 30000; + private static Vector defaultMechs = new Vector(); private SmackConfiguration() { } @@ -89,6 +92,9 @@ public final class SmackConfiguration { else if (parser.getName().equals("keepAliveInterval")) { keepAliveInterval = parseIntProperty(parser, keepAliveInterval); } + else if (parser.getName().equals("mechName")) { + defaultMechs.add(parser.nextText()); + } } eventType = parser.next(); } @@ -173,6 +179,61 @@ public final class SmackConfiguration { keepAliveInterval = interval; } + /** + * Add a SASL mechanism to the list to be used. + * + * @param mech the SASL mechanism to be added + */ + public static void addSaslMech(String mech) { + if(! defaultMechs.contains(mech) ) { + defaultMechs.add(mech); + } + } + + /** + * Add a Collection of SASL mechanisms to the list to be used. + * + * @param mechs the Collection of SASL mechanisms to be added + */ + public static void addSaslMechs(Collection mechs) { + for(String mech : mechs) { + addSaslMech(mech); + } + } + + /** + * Remove a SASL mechanism from the list to be used. + * + * @param mech the SASL mechanism to be removed + */ + public static void removeSaslMech(String mech) { + if( defaultMechs.contains(mech) ) { + defaultMechs.remove(mech); + } + } + + /** + * Remove a Collection of SASL mechanisms to the list to be used. + * + * @param mechs the Collection of SASL mechanisms to be removed + */ + public static void removeSaslMechs(Collection mechs) { + for(String mech : mechs) { + removeSaslMech(mech); + } + } + + /** + * Returns the list of SASL mechanisms to be used. If a SASL mechanism is + * listed here it does not guarantee it will be used. The server may not + * support it, or it may not be implemented. + * + * @return the list of SASL mechanisms to be used. + */ + public static List getSaslMechs() { + return defaultMechs; + } + private static void parseClassToLoad(XmlPullParser parser) throws Exception { String className = parser.nextText(); // Attempt to load the class so that the class can get initialized diff --git a/source/org/jivesoftware/smack/UserAuthentication.java b/source/org/jivesoftware/smack/UserAuthentication.java index 6b2ce3992..e2d1e1475 100644 --- a/source/org/jivesoftware/smack/UserAuthentication.java +++ b/source/org/jivesoftware/smack/UserAuthentication.java @@ -20,20 +20,44 @@ package org.jivesoftware.smack; +import javax.security.auth.callback.CallbackHandler; + /** * There are two ways to authenticate a user with a server. Using SASL or Non-SASL * authentication. This interface makes {@link SASLAuthentication} and * {@link NonSASLAuthentication} polyphormic. * * @author Gaston Dombiak + * @author Jay Kline */ interface UserAuthentication { + /** + * Authenticates the user with the server. This method will return the full JID provided by + * the server. The server may assign a full JID with a username and resource different than + * requested by this method. + * + * Note that using callbacks is the prefered method of authenticating users since it allows + * more flexability in the mechanisms used. + * + * @param username the requested username (authorization ID) for authenticating to the server + * @param resource the requested resource. + * @param cbh the CallbackHandler used to obtain authentication ID, password, or other + * information + * @return the full JID provided by the server while binding a resource for the connection. + * @throws XMPPException if an error occurs while authenticating. + */ + String authenticate(String username, String resource, CallbackHandler cbh) throws + XMPPException; + /** * Authenticates the user with the server. This method will return the full JID provided by * the server. The server may assign a full JID with a username and resource different than * the requested by this method. * + * It is recommended that @{link #authenticate(String, String, CallbackHandler)} be used instead + * since it provides greater flexability in authenticaiton and authorization. + * * @param username the username that is authenticating with the server. * @param password the password to send to the server. * @param resource the desired resource. diff --git a/source/org/jivesoftware/smack/XMPPConnection.java b/source/org/jivesoftware/smack/XMPPConnection.java index 0044d66c2..61bce66a5 100644 --- a/source/org/jivesoftware/smack/XMPPConnection.java +++ b/source/org/jivesoftware/smack/XMPPConnection.java @@ -29,6 +29,9 @@ import org.jivesoftware.smack.util.StringUtils; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; +import java.security.KeyStore; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.KeyManager; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; @@ -39,6 +42,7 @@ import java.util.Collection; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; +import javax.security.auth.callback.CallbackHandler; /** * Creates a connection to a XMPP server. A simple use of this API might @@ -275,6 +279,8 @@ public class XMPPConnection { * (default timeout) elapses in each step of the authentication process without * a response from the server, or if an error occurs, a XMPPException will be thrown. * + * It is recommended to use the {@link #login(String, CallbackHandler)} instead. + * * @param username the username. * @param password the password. * @throws XMPPException if an error occurs. @@ -289,6 +295,24 @@ public class XMPPConnection { * (default timeout) elapses in each step of the authentication process without * a response from the server, or if an error occurs, a XMPPException will be thrown. * + * It is recommended to use the {@link #login(String, CallbackHandler)} instead. + * + * @param username the username. + * @param cbh The CallbackHandler used to determine password, or other information. + * @throws XMPPException if an error occurs. + */ + public void login(String username, CallbackHandler cbh) throws XMPPException { + login(username, "Smack", cbh); + } + + /** + * Logs in to the server using the strongest authentication mode supported by + * the server, then sets presence to available. If more than five seconds + * (default timeout) elapses in each step of the authentication process without + * a response from the server, or if an error occurs, a XMPPException will be thrown. + * + * It is recommended to use the {@link #login(String, String, CallbackHandler)} instead. + * * @param username the username. * @param password the password. * @param resource the resource. @@ -301,6 +325,24 @@ public class XMPPConnection { login(username, password, resource, true); } + /** + * Logs in to the server using the strongest authentication mode supported by + * the server, then sets presence to available. If more than five seconds + * (default timeout) elapses in each step of the authentication process without + * a response from the server, or if an error occurs, a XMPPException will be thrown. + * + * @param username the username. + * @param cbh the password. + * @param resource the resource. + * @throws XMPPException if an error occurs. + * @throws IllegalStateException if not connected to the server, or already logged in + * to the serrver. + */ + public synchronized void login(String username, String resource, CallbackHandler cbh) + throws XMPPException { + login(username, resource, true, cbh); + } + /** * Logs in to the server using the strongest authentication mode supported by * the server. If the server supports SASL authentication then the user will be @@ -314,6 +356,8 @@ public class XMPPConnection { * For compatibility and easiness of use the connection will automatically connect to the * server if not already connected. * + * It is recommended to use the {@link #login(String, String, boolean, CallbackHandler)} instead. + * * @param username the username. * @param password the password. * @param resource the resource. @@ -389,6 +433,100 @@ public class XMPPConnection { debugger.userHasLogged(user); } } + /** + * Logs in to the server using the strongest authentication mode supported by + * the server. If the server supports SASL authentication then the user will be + * authenticated using SASL if not Non-SASL authentication will be tried. An available + * presence may optionally be sent. If sendPresence + * is false, a presence packet must be sent manually later. If more than five seconds + * (default timeout) elapses in each step of the authentication process without a + * response from the server, or if an error occurs, a XMPPException will be thrown.

+ *

+ * Before logging in (i.e. authenticate) to the server the connection must be connected. + * For compatibility and easiness of use the connection will automatically connect to the + * server if not already connected. + * + * This version requires the use of a CallbackHandler to obtain information, such as the + * password or principal information + * + * @param username the username. + * @param cbh the callback handler. + * @param resource the resource. + * @param sendPresence if true an available presence will be sent automatically + * after login is completed. + * @throws XMPPException if an error occurs. + * @throws IllegalStateException if not connected to the server, or already logged in + * to the serrver. + */ + public synchronized void login(String username, String resource, + boolean sendPresence, CallbackHandler cbh) throws XMPPException { + + + if (!isConnected()) { + throw new IllegalStateException("Not connected to server."); + } + if (authenticated) { + throw new IllegalStateException("Already logged in to server."); + } + // Do partial version of nameprep on the username. + username = username.toLowerCase().trim(); + + String response; + if (configuration.isSASLAuthenticationEnabled() && + saslAuthentication.hasNonAnonymousAuthentication()) { + // Authenticate using SASL + response = saslAuthentication.authenticate(username, resource, cbh); + } + else { + throw new XMPPException("SASL authentication unavilable"); + } + + // Set the user. + if (response != null) { + this.user = response; + // Update the serviceName with the one returned by the server + this.serviceName = StringUtils.parseServer(response); + } + else { + this.user = username + "@" + this.serviceName; + if (resource != null) { + this.user += "/" + resource; + } + } + + // If compression is enabled then request the server to use stream compression + if (configuration.isCompressionEnabled()) { + useCompression(); + } + + // Create the roster if it is not a reconnection. + if (this.roster == null) { + this.roster = new Roster(this); + } + roster.reload(); + + // Set presence to online. + if (sendPresence) { + packetWriter.sendPacket(new Presence(Presence.Type.available)); + } + + // Indicate that we're now authenticated. + authenticated = true; + anonymous = false; + + //TODO: Handle this! + // Stores the autentication for future reconnection + //this.getConfiguration().setLoginInfo(username, password, resource, sendPresence); + + // If debugging is enabled, change the the debug window title to include the + // name we are now logged-in as. + // If DEBUG_ENABLED was set to true AFTER the connection was created the debugger + // will be null + if (configuration.isDebuggerEnabled() && debugger != null) { + debugger.userHasLogged(user); + } + + } /** * Logs in to the server anonymously. Very few servers are configured to support anonymous @@ -1111,8 +1249,21 @@ public class XMPPConnection { */ void proceedTLSReceived() throws Exception { SSLContext context = SSLContext.getInstance("TLS"); + KeyStore ks = KeyStore.getInstance(configuration.getKeystoreType()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + KeyManager[] kms = null; + try { + ks.load(new FileInputStream(configuration.getKeystorePath()), configuration.getKeystorePassword().toCharArray()); + kmf.init(ks,configuration.getKeystorePassword().toCharArray()); + kms = kmf.getKeyManagers(); + } catch (FileNotFoundException fourohfour) { + kms = null; + } catch (NullPointerException npe) { + kms = null; + } + // Verify certificate presented by the server - context.init(null, // KeyManager not required + context.init(kms, new javax.net.ssl.TrustManager[]{new ServerTrustManager(serviceName, configuration)}, new java.security.SecureRandom()); Socket plain = socket; @@ -1125,7 +1276,15 @@ public class XMPPConnection { initReaderAndWriter(); // Proceed to do the handshake ((SSLSocket) socket).startHandshake(); - + if (((SSLSocket) socket).getWantClientAuth()) { + System.err.println("Connection wants client auth"); + } + else if (((SSLSocket) socket).getNeedClientAuth()) { + System.err.println("Connection needs client auth"); + } + else { + System.err.println("Connection does not requrie client auth"); + } // Set that TLS was successful usingTLS = true; @@ -1304,4 +1463,4 @@ public class XMPPConnection { this.wasAuthenticated = wasAuthenticated; } } -} \ No newline at end of file +} diff --git a/source/org/jivesoftware/smack/sasl/SASLAnonymous.java b/source/org/jivesoftware/smack/sasl/SASLAnonymous.java index 7a03d9c0e..81e3043a1 100644 --- a/source/org/jivesoftware/smack/sasl/SASLAnonymous.java +++ b/source/org/jivesoftware/smack/sasl/SASLAnonymous.java @@ -3,7 +3,6 @@ * $Revision: $ * $Date: $ * - * Copyright 2003-2005 Jive Software. * * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +21,13 @@ package org.jivesoftware.smack.sasl; import org.jivesoftware.smack.SASLAuthentication; +import java.io.IOException; +import javax.security.auth.callback.CallbackHandler; + /** - * Implementation of the SASL ANONYMOUS mechanisn as defined by the - * IETF draft - * document. + * Implementation of the SASL ANONYMOUS mechanism * - * @author Gaston Dombiak + * @author Jay Kline */ public class SASLAnonymous extends SASLMechanism { @@ -39,14 +39,34 @@ public class SASLAnonymous extends SASLMechanism { return "ANONYMOUS"; } - protected String getAuthenticationText(String username, String host, String password) { - // Nothing to send in the body - return null; + public void authenticate(String username, String host, CallbackHandler cbh) throws IOException { + authenticate(); } - protected String getChallengeResponse(byte[] bytes) { - // Some servers may send a challenge to gather more information such as - // email address. Return any string value. - return "anything"; + public void authenticate(String username, String host, String password) throws IOException { + authenticate(); } + + protected void authenticate() throws IOException { + StringBuffer stanza = new StringBuffer(); + stanza.append(""); + stanza.append(""); + + // Send the authentication to the server + getSASLAuthentication().send(stanza.toString()); + } + + public void challengeReceived(String challenge) throws IOException { + // Build the challenge response stanza encoding the response text + StringBuffer stanza = new StringBuffer(); + stanza.append(""); + stanza.append("="); + stanza.append(""); + + // Send the authentication to the server + getSASLAuthentication().send(stanza.toString()); + } + + } diff --git a/source/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java b/source/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java new file mode 100644 index 000000000..75d497024 --- /dev/null +++ b/source/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java @@ -0,0 +1,38 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: $ + * + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.sasl; + +import org.jivesoftware.smack.SASLAuthentication; + +/** + * Implementation of the SASL CRAM-MD5 mechanism + * + * @author Jay Kline + */ +public class SASLCramMD5Mechanism extends SASLMechanism { + + public SASLCramMD5Mechanism(SASLAuthentication saslAuthentication) { + super(saslAuthentication); + } + + protected String getName() { + return "CRAM-MD5"; + } +} diff --git a/source/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java b/source/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java new file mode 100644 index 000000000..7621b7d40 --- /dev/null +++ b/source/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java @@ -0,0 +1,38 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: $ + * + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.sasl; + +import org.jivesoftware.smack.SASLAuthentication; + +/** + * Implementation of the SASL DIGEST-MD5 mechanism + * + * @author Jay Kline + */ +public class SASLDigestMD5Mechanism extends SASLMechanism { + + public SASLDigestMD5Mechanism(SASLAuthentication saslAuthentication) { + super(saslAuthentication); + } + + protected String getName() { + return "CRAM-MD5"; + } +} diff --git a/source/org/jivesoftware/smack/sasl/SASLExternalMechanism.java b/source/org/jivesoftware/smack/sasl/SASLExternalMechanism.java new file mode 100644 index 000000000..a2a3f6b06 --- /dev/null +++ b/source/org/jivesoftware/smack/sasl/SASLExternalMechanism.java @@ -0,0 +1,59 @@ +/** + * $RCSfile$ + * $Revision: $ + * $Date: $ + * + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack.sasl; + +import org.jivesoftware.smack.SASLAuthentication; + +/** + * Implementation of the SASL EXTERNAL mechanism. + * + * To effectively use this mechanism, Java must be configured to properly + * supply a client SSL certificate (of some sort) to the server. It is up + * to the implementer to determine how to do this. Here is one method: + * + * Create a java keystore with your SSL certificate in it: + * keytool -genkey -alias username -dname "cn=username,ou=organizationalUnit,o=organizationaName,l=locality,s=state,c=country" + * + * Next, set the System Properties: + *

+ * + * Then, when the server requests or requires the client certificate, java will + * simply provide the one in the keyStore. + * + * Also worth noting is the EXTERNAL mechanism in Smack is not enabled by default. + * To enable it, the implementer will need to call SASLAuthentication.supportSASLMechamism("EXTERNAL"); + * + * @author Jay Kline + */ +public class SASLExternalMechanism extends SASLMechanism { + + public SASLExternalMechanism(SASLAuthentication saslAuthentication) { + super(saslAuthentication); + } + + protected String getName() { + return "EXTERNAL"; + } +} diff --git a/source/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java b/source/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java index 9a45b98da..2cdc21e0a 100644 --- a/source/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java +++ b/source/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java @@ -19,29 +19,26 @@ package org.jivesoftware.smack.sasl; -import javax.security.sasl.*; -import java.util.*; -import java.io.IOException; - import org.jivesoftware.smack.SASLAuthentication; -import org.jivesoftware.smack.util.Base64; +import org.jivesoftware.smack.XMPPException; + +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.auth.callback.CallbackHandler; /** - * Implementation of the SASL GSSAPI mechanisn - * + * Implementation of the SASL GSSAPI mechanism * * @author Jay Kline */ public class SASLGSSAPIMechanism extends SASLMechanism { - private static final String protocol = "xmpp"; - private static final String[] mechanisms = {"GSSAPI"}; - private SaslClient sc; - public SASLGSSAPIMechanism(SASLAuthentication saslAuthentication) { super(saslAuthentication); - - System.setProperty("java.security.krb5.debug","true"); + System.setProperty("javax.security.auth.useSubjectCredsOnly","false"); System.setProperty("java.security.auth.login.config","gss.conf"); @@ -59,71 +56,35 @@ public class SASLGSSAPIMechanism extends SASLMechanism { * * @param username the username of the user being authenticated. * @param host the hostname where the user account resides. - * @param password the password of the user (ignored for GSSAPI) + * @param cbh the CallbackHandler (not used with GSSAPI) * @throws IOException If a network error occures while authenticating. */ - public void authenticate(String username, String host, String password) throws IOException { - // Build the authentication stanza encoding the authentication text - StringBuffer stanza = new StringBuffer(); + public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException { + String[] mechanisms = { getName() }; Map props = new HashMap(); - - sc = Sasl.createSaslClient(mechanisms, username, protocol, host, props, null); - - stanza.append(""); - if(sc.hasInitialResponse()) { - byte[] response = sc.evaluateChallenge(new byte[0]); - String authenticationText = Base64.encodeBytes(response,Base64.DONT_BREAK_LINES); - if(authenticationText != null && !authenticationText.equals("")) { - stanza.append(authenticationText); - } - } - stanza.append(""); - - // Send the authentication to the server - getSASLAuthentication().send(stanza.toString()); - } - - - protected String getAuthenticationText(String username, String host, String password) { - // Unused, see authenticate - return null; + props.put(Sasl.SERVER_AUTH,"TRUE"); + sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh); + authenticate(); } /** - * The server is challenging the SASL mechanism for the stanza he just sent. Send a - * response to the server's challenge. This overrieds from the abstract class because the - * tokens needed for GSSAPI are binary, and not safe to put in a string, thus - * getChallengeResponse() cannot be used. + * Builds and sends the auth stanza to the server. + * This overrides from the abstract class because the initial token + * needed for GSSAPI is binary, and not safe to put in a string, thus + * getAuthenticationText() cannot be used. * - * @param challenge a base64 encoded string representing the challenge. + * @param username the username of the user being authenticated. + * @param host the hostname where the user account resides. + * @param password the password of the user (ignored for GSSAPI) + * @throws IOException If a network error occures while authenticating. */ - public void challengeReceived(String challenge) throws IOException { - // Build the challenge response stanza encoding the response text - StringBuffer stanza = new StringBuffer(); - - byte response[]; - if(challenge != null) { - response = sc.evaluateChallenge(Base64.decode(challenge)); - } else { - response = sc.evaluateChallenge(null); - } - - String authenticationText = Base64.encodeBytes(response,Base64.DONT_BREAK_LINES); - if(authenticationText.equals("")) { - authenticationText = "="; - } - - stanza.append(""); - stanza.append(authenticationText); - stanza.append(""); - - // Send the authentication to the server - getSASLAuthentication().send(stanza.toString()); + public void authenticate(String username, String host, String password) throws IOException, XMPPException { + String[] mechanisms = { getName() }; + Map props = new HashMap(); + props.put(Sasl.SERVER_AUTH,"TRUE"); + sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this); + authenticate(); } - protected String getChallengeResponse(byte[] bytes) { - // Unused, see challengeReceived - return null; - } + } diff --git a/source/org/jivesoftware/smack/sasl/SASLMechanism.java b/source/org/jivesoftware/smack/sasl/SASLMechanism.java index adf7e7a06..d16fd5ba1 100644 --- a/source/org/jivesoftware/smack/sasl/SASLMechanism.java +++ b/source/org/jivesoftware/smack/sasl/SASLMechanism.java @@ -20,47 +20,107 @@ package org.jivesoftware.smack.sasl; +import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.SASLAuthentication; -import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smack.util.Base64; import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.sasl.RealmCallback; +import javax.security.sasl.RealmChoiceCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; /** - * Base class for SASL mechanisms. Subclasses must implement three methods: + * Base class for SASL mechanisms. Subclasses must implement these methods: * + * Subclasses will likely want to implement their own versions of these mthods: + *
  • {@link #authenticate(String, String, String)} -- Initiate authentication stanza using the + * deprecated method.
  • + *
  • {@link #authenticate(String, String, CallbackHandler)} -- Initiate authentication stanza + * using the CallbackHandler method.
  • + *
  • {@link #challengeReceived(String)} -- Handle a challenge from the server.
  • * * - * @author Gaston Dombiak + * @author Jay Kline */ -public abstract class SASLMechanism { +public abstract class SASLMechanism implements CallbackHandler { private SASLAuthentication saslAuthentication; + protected SaslClient sc; + protected String authenticationId; + protected String password; + public SASLMechanism(SASLAuthentication saslAuthentication) { - super(); this.saslAuthentication = saslAuthentication; } /** - * Builds and sends the auth stanza to the server. + * Builds and sends the auth stanza to the server. Note that this method of + * authentication is not recommended, since it is very inflexable. Use + * {@link #authenticate(String, String, CallbackHandler)} whenever possible. * * @param username the username of the user being authenticated. * @param host the hostname where the user account resides. - * @param password the password of the user. - * @throws IOException If a network error occures while authenticating. + * @param password the password for this account. + * @throws IOException If a network error occurs while authenticating. + * @throws XMPPException If a protocol error occurs or the user is not authenticated. */ - public void authenticate(String username, String host, String password) throws IOException { - // Build the authentication stanza encoding the authentication text - StringBuilder stanza = new StringBuilder(); + public void authenticate(String username, String host, String password) throws IOException, XMPPException { + //Since we were not provided with a CallbackHandler, we will use our own with the given + //information + + //Set the authenticationID as the username, since they must be the same in this case. + this.authenticationId = username; + this.password = password; + + String[] mechanisms = { getName() }; + Map props = new HashMap(); + sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this); + authenticate(); + } + + /** + * Builds and sends the auth stanza to the server. The callback handler will handle + * any additional information, such as the authentication ID or realm, if it is needed. + * + * @param username the username of the user being authenticated. + * @param host the hostname where the user account resides. + * @param cbh the CallbackHandler to obtain user information. + * @throws IOException If a network error occures while authenticating. + * @throws XMPPException If a protocol error occurs or the user is not authenticated. + */ + public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException { + String[] mechanisms = { getName() }; + Map props = new HashMap(); + sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh); + authenticate(); + } + + protected void authenticate() throws IOException, XMPPException { + StringBuffer stanza = new StringBuffer(); stanza.append(""); - String authenticationText = getAuthenticationText(username, host, password); - if (authenticationText != null) { - stanza.append(StringUtils.encodeBase64(authenticationText)); + try { + if(sc.hasInitialResponse()) { + byte[] response = sc.evaluateChallenge(new byte[0]); + String authenticationText = Base64.encodeBytes(response,Base64.DONT_BREAK_LINES); + if(authenticationText != null && !authenticationText.equals("")) { + stanza.append(authenticationText); + } + } + } catch (SaslException e) { + throw new XMPPException("SASL authentication failed", e); } stanza.append(""); @@ -68,6 +128,7 @@ public abstract class SASLMechanism { getSASLAuthentication().send(stanza.toString()); } + /** * The server is challenging the SASL mechanism for the stanza he just sent. Send a * response to the server's challenge. @@ -77,12 +138,22 @@ public abstract class SASLMechanism { */ public void challengeReceived(String challenge) throws IOException { // Build the challenge response stanza encoding the response text - StringBuilder stanza = new StringBuilder(); - stanza.append(""); - String authenticationText = getChallengeResponse(StringUtils.decodeBase64(challenge)); - if (authenticationText != null) { - stanza.append(StringUtils.encodeBase64(authenticationText)); + StringBuffer stanza = new StringBuffer(); + + byte response[]; + if(challenge != null) { + response = sc.evaluateChallenge(Base64.decode(challenge)); + } else { + response = sc.evaluateChallenge(null); } + + String authenticationText = Base64.encodeBytes(response,Base64.DONT_BREAK_LINES); + if(authenticationText.equals("")) { + authenticationText = "="; + } + + stanza.append(""); + stanza.append(authenticationText); stanza.append(""); // Send the authentication to the server @@ -90,34 +161,37 @@ public abstract class SASLMechanism { } /** - * Returns the response text to send answering the challenge sent by the server. Mechanisms - * that will never receive a challenge may redefine this method returning null. - * - * @param bytes the challenge sent by the server. - * @return the response text to send to answer the challenge sent by the server. - */ - protected abstract String getChallengeResponse(byte[] bytes); - - /** - * Returns the common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or KERBEROS_V4. + * Returns the common name of the SASL mechanism. E.g.: PLAIN, DIGEST-MD5 or GSSAPI. * * @return the common name of the SASL mechanism. */ protected abstract String getName(); - /** - * Returns the authentication text to include in the initial auth stanza - * or null if nothing should be added. - * - * @param username the username of the user being authenticated. - * @param host the hostname where the user account resides. - * @param password the password of the user. - * @return the authentication text to include in the initial auth stanza - * or null if nothing should be added. - */ - protected abstract String getAuthenticationText(String username, String host, String password); protected SASLAuthentication getSASLAuthentication() { return saslAuthentication; } + + /** + * + */ + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + NameCallback ncb = (NameCallback)callbacks[i]; + ncb.setName(authenticationId); + } else if(callbacks[i] instanceof PasswordCallback) { + PasswordCallback pcb = (PasswordCallback)callbacks[i]; + pcb.setPassword(password.toCharArray()); + } else if(callbacks[i] instanceof RealmCallback) { + //unused + //RealmCallback rcb = (RealmCallback)callbacks[i]; + } else if(callbacks[i] instanceof RealmChoiceCallback){ + //unused + //RealmChoiceCallback rccb = (RealmChoiceCallback)callbacks[i]; + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } } diff --git a/source/org/jivesoftware/smack/sasl/SASLPlainMechanism.java b/source/org/jivesoftware/smack/sasl/SASLPlainMechanism.java index a06e2e9d9..cd973eb87 100644 --- a/source/org/jivesoftware/smack/sasl/SASLPlainMechanism.java +++ b/source/org/jivesoftware/smack/sasl/SASLPlainMechanism.java @@ -1,5 +1,4 @@ /** - * Copyright 2003-2007 Jive Software. * * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +18,9 @@ package org.jivesoftware.smack.sasl; import org.jivesoftware.smack.SASLAuthentication; /** - * Implementation of the SASL PLAIN mechanisn as defined by the - * IETF draft - * document. + * Implementation of the SASL PLAIN mechanism * - * @author Gaston Dombiak + * @author Jay Kline */ public class SASLPlainMechanism extends SASLMechanism { @@ -34,23 +31,4 @@ public class SASLPlainMechanism extends SASLMechanism { protected String getName() { return "PLAIN"; } - - protected String getAuthenticationText(String username, String host, String password) { - // Build the text containing the "authorization identity" + NUL char + - // "authentication identity" + NUL char + "clear-text password" - StringBuilder text = new StringBuilder(); - // Commented out line below due to SMACK-224. This part of PLAIN auth seems to be - // optional, and just removing it should increase compatability. -// text.append(username).append("@").append(host); - text.append('\0'); - text.append(username); - text.append('\0'); - text.append(password); - return text.toString(); - } - - protected String getChallengeResponse(byte[] bytes) { - // Return null since this mechanism will never get a challenge from the server - return null; - } } diff --git a/test/org/jivesoftware/smack/LoginTest.java b/test/org/jivesoftware/smack/LoginTest.java index 7ddef3def..21a6ecd35 100644 --- a/test/org/jivesoftware/smack/LoginTest.java +++ b/test/org/jivesoftware/smack/LoginTest.java @@ -1,7 +1,7 @@ /** * $RCSfile$ - * $Revision: $ - * $Date: $ + * $Revision$ + * $Date$ * * Copyright 2003-2007 Jive Software. * @@ -154,7 +154,7 @@ public class LoginTest extends SmackTestCase { throw e; } } - conn.login("user_1", "user_1", null); + conn.login("user_1", "user_1", (String) null); if (conn.getSASLAuthentication().isAuthenticated()) { // Check that the server assigned a resource assertNotNull("JID assigned by server is missing", conn.getUser());