aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smack/sasl
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smack/sasl')
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLAnonymous.java62
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java38
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java38
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java59
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLFacebookConnect.java201
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java89
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLMechanism.java323
-rw-r--r--src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java34
-rw-r--r--src/org/jivesoftware/smack/sasl/package.html1
9 files changed, 845 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/sasl/SASLAnonymous.java b/src/org/jivesoftware/smack/sasl/SASLAnonymous.java
new file mode 100644
index 0000000..a1b2c88
--- /dev/null
+++ b/src/org/jivesoftware/smack/sasl/SASLAnonymous.java
@@ -0,0 +1,62 @@
+/**
+ * $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;
+
+import java.io.IOException;
+import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
+
+/**
+ * Implementation of the SASL ANONYMOUS mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLAnonymous extends SASLMechanism {
+
+ public SASLAnonymous(SASLAuthentication saslAuthentication) {
+ super(saslAuthentication);
+ }
+
+ protected String getName() {
+ return "ANONYMOUS";
+ }
+
+ public void authenticate(String username, String host, CallbackHandler cbh) throws IOException {
+ authenticate();
+ }
+
+ public void authenticate(String username, String host, String password) throws IOException {
+ authenticate();
+ }
+
+ protected void authenticate() throws IOException {
+ // Send the authentication to the server
+ getSASLAuthentication().send(new AuthMechanism(getName(), null));
+ }
+
+ public void challengeReceived(String challenge) throws IOException {
+ // Build the challenge response stanza encoding the response text
+ // and send the authentication to the server
+ getSASLAuthentication().send(new Response());
+ }
+
+
+}
diff --git a/src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java b/src/org/jivesoftware/smack/sasl/SASLCramMD5Mechanism.java
new file mode 100644
index 0000000..82d218f
--- /dev/null
+++ b/src/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/src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java b/src/org/jivesoftware/smack/sasl/SASLDigestMD5Mechanism.java
new file mode 100644
index 0000000..7af65fb
--- /dev/null
+++ b/src/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 "DIGEST-MD5";
+ }
+}
diff --git a/src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java b/src/org/jivesoftware/smack/sasl/SASLExternalMechanism.java
new file mode 100644
index 0000000..dff18fb
--- /dev/null
+++ b/src/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:
+ * <ul>
+ * <li>javax.net.ssl.keyStore to the location of the keyStore
+ * <li>javax.net.ssl.keyStorePassword to the password of the keyStore
+ * <li>javax.net.ssl.trustStore to the location of the trustStore
+ * <li>javax.net.ssl.trustStorePassword to the the password of the trustStore
+ * </ul>
+ *
+ * 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/src/org/jivesoftware/smack/sasl/SASLFacebookConnect.java b/src/org/jivesoftware/smack/sasl/SASLFacebookConnect.java
new file mode 100644
index 0000000..3126d83
--- /dev/null
+++ b/src/org/jivesoftware/smack/sasl/SASLFacebookConnect.java
@@ -0,0 +1,201 @@
+package org.jivesoftware.smack.sasl;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
+import de.measite.smack.Sasl;
+
+import org.jivesoftware.smack.SASLAuthentication;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.sasl.SASLMechanism;
+import org.jivesoftware.smack.util.Base64;
+
+/**
+ * This class is originally from http://code.google.com/p/fbgc/source/browse/trunk/daemon/src/main/java/org/albino/mechanisms/FacebookConnectSASLMechanism.java
+ * I just adapted to match the SMACK package scheme and
+ */
+public class SASLFacebookConnect extends SASLMechanism {
+
+ private String sessionKey = "";
+ private String sessionSecret = "";
+ private String apiKey = "";
+
+ static{
+ SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM",
+ SASLFacebookConnect.class);
+ SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
+ }
+
+ public SASLFacebookConnect(SASLAuthentication saslAuthentication) {
+ super(saslAuthentication);
+ }
+
+ // protected void authenticate() throws IOException, XMPPException {
+ // String[] mechanisms = { getName() };
+ // Map<String, String> props = new HashMap<String, String>();
+ // sc = Sasl.createSaslClient(mechanisms, null, "xmpp", hostname, props,
+ // this);
+ //
+ // super.authenticate();
+ // }
+
+ protected void authenticate() throws IOException, XMPPException {
+ final StringBuilder stanza = new StringBuilder();
+ stanza.append("<auth mechanism=\"").append(getName());
+ stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ stanza.append("</auth>");
+
+ // Send the authentication to the server
+ getSASLAuthentication().send(new Packet(){
+
+ @Override
+ public String toXML() {
+ return stanza.toString();
+ }
+
+ });
+ }
+
+ public void authenticate(String apiKeyAndSessionKey, String host, String sessionSecret)
+ throws IOException, XMPPException {
+
+ if(apiKeyAndSessionKey==null || sessionSecret==null)
+ throw new IllegalStateException("Invalid parameters!");
+
+ String[] keyArray = apiKeyAndSessionKey.split("\\|");
+
+ if(keyArray==null || keyArray.length != 2)
+ throw new IllegalStateException("Api key or session key is not present!");
+
+ this.apiKey = keyArray[0];
+ this.sessionKey = keyArray[1];
+ this.sessionSecret = sessionSecret;
+
+ this.authenticationId = sessionKey;
+ this.password = sessionSecret;
+ this.hostname = host;
+
+ String[] mechanisms = { "DIGEST-MD5" };
+ Map<String, String> props = new HashMap<String, String>();
+ sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
+ authenticate();
+ }
+
+ public void authenticate(String username, String host, CallbackHandler cbh)
+ throws IOException, XMPPException {
+ String[] mechanisms = { "DIGEST-MD5" };
+ Map<String, String> props = new HashMap<String, String>();
+ sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
+ authenticate();
+ }
+
+ protected String getName() {
+ return "X-FACEBOOK-PLATFORM";
+ }
+
+ public void challengeReceived(String challenge) throws IOException {
+ // Build the challenge response stanza encoding the response text
+ final StringBuilder stanza = new StringBuilder();
+
+ byte response[] = null;
+ if (challenge != null) {
+ String decodedResponse = new String(Base64.decode(challenge));
+ Map<String, String> parameters = getQueryMap(decodedResponse);
+
+ String version = "1.0";
+ String nonce = parameters.get("nonce");
+ String method = parameters.get("method");
+
+ Long callId = new GregorianCalendar().getTimeInMillis()/1000;
+
+ String sig = "api_key="+apiKey
+ +"call_id="+callId
+ +"method="+method
+ +"nonce="+nonce
+ +"session_key="+sessionKey
+ +"v="+version
+ +sessionSecret;
+
+ try {
+ sig = MD5(sig);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ }
+
+ String composedResponse = "api_key="+apiKey+"&"
+ +"call_id="+callId+"&"
+ +"method="+method+"&"
+ +"nonce="+nonce+"&"
+ +"session_key="+sessionKey+"&"
+ +"v="+version+"&"
+ +"sig="+sig;
+
+ response = composedResponse.getBytes();
+ }
+
+ String authenticationText="";
+
+ if (response != null) {
+ authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
+ }
+
+ stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ stanza.append(authenticationText);
+ stanza.append("</response>");
+
+ // Send the authentication to the server
+ getSASLAuthentication().send(new Packet(){
+
+ @Override
+ public String toXML() {
+ return stanza.toString();
+ }
+
+ });
+ }
+
+ private Map<String, String> getQueryMap(String query) {
+ String[] params = query.split("&");
+ Map<String, String> map = new HashMap<String, String>();
+ for (String param : params) {
+ String name = param.split("=")[0];
+ String value = param.split("=")[1];
+ map.put(name, value);
+ }
+ return map;
+ }
+
+ private String convertToHex(byte[] data) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < data.length; i++) {
+ int halfbyte = (data[i] >>> 4) & 0x0F;
+ int two_halfs = 0;
+ do {
+ if ((0 <= halfbyte) && (halfbyte <= 9))
+ buf.append((char) ('0' + halfbyte));
+ else
+ buf.append((char) ('a' + (halfbyte - 10)));
+ halfbyte = data[i] & 0x0F;
+ } while(two_halfs++ < 1);
+ }
+ return buf.toString();
+ }
+
+ public String MD5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ MessageDigest md;
+ md = MessageDigest.getInstance("MD5");
+ byte[] md5hash = new byte[32];
+ md.update(text.getBytes("iso-8859-1"), 0, text.length());
+ md5hash = md.digest();
+ return convertToHex(md5hash);
+ }
+}
+
diff --git a/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java b/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java
new file mode 100644
index 0000000..e8a4967
--- /dev/null
+++ b/src/org/jivesoftware/smack/sasl/SASLGSSAPIMechanism.java
@@ -0,0 +1,89 @@
+/**
+ * $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;
+import org.jivesoftware.smack.XMPPException;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import de.measite.smack.Sasl;
+import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
+
+/**
+ * Implementation of the SASL GSSAPI mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLGSSAPIMechanism extends SASLMechanism {
+
+ public SASLGSSAPIMechanism(SASLAuthentication saslAuthentication) {
+ super(saslAuthentication);
+
+ System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
+ System.setProperty("java.security.auth.login.config","gss.conf");
+
+ }
+
+ protected String getName() {
+ return "GSSAPI";
+ }
+
+ /**
+ * Builds and sends the <tt>auth</tt> 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 username the username of the user being authenticated.
+ * @param host the hostname where the user account resides.
+ * @param cbh the CallbackHandler (not used with GSSAPI)
+ * @throws IOException If a network error occures while authenticating.
+ */
+ public void authenticate(String username, String host, CallbackHandler cbh) throws IOException, XMPPException {
+ String[] mechanisms = { getName() };
+ Map<String,String> props = new HashMap<String,String>();
+ props.put(Sasl.SERVER_AUTH,"TRUE");
+ sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);
+ authenticate();
+ }
+
+ /**
+ * Builds and sends the <tt>auth</tt> 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 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 authenticate(String username, String host, String password) throws IOException, XMPPException {
+ String[] mechanisms = { getName() };
+ Map<String,String> props = new HashMap<String, String>();
+ props.put(Sasl.SERVER_AUTH,"TRUE");
+ sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
+ authenticate();
+ }
+
+
+}
diff --git a/src/org/jivesoftware/smack/sasl/SASLMechanism.java b/src/org/jivesoftware/smack/sasl/SASLMechanism.java
new file mode 100644
index 0000000..3aeba86
--- /dev/null
+++ b/src/org/jivesoftware/smack/sasl/SASLMechanism.java
@@ -0,0 +1,323 @@
+/**
+ * $RCSfile$
+ * $Revision$
+ * $Date$
+ *
+ * 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.
+ * 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.XMPPException;
+import org.jivesoftware.smack.SASLAuthentication;
+import org.jivesoftware.smack.packet.Packet;
+import org.jivesoftware.smack.util.StringUtils;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
+import org.apache.harmony.javax.security.auth.callback.UnsupportedCallbackException;
+import org.apache.harmony.javax.security.auth.callback.Callback;
+import org.apache.harmony.javax.security.auth.callback.NameCallback;
+import org.apache.harmony.javax.security.auth.callback.PasswordCallback;
+import org.apache.harmony.javax.security.sasl.RealmCallback;
+import org.apache.harmony.javax.security.sasl.RealmChoiceCallback;
+import de.measite.smack.Sasl;
+import org.apache.harmony.javax.security.sasl.SaslClient;
+import org.apache.harmony.javax.security.sasl.SaslException;
+
+/**
+ * Base class for SASL mechanisms. Subclasses must implement these methods:
+ * <ul>
+ * <li>{@link #getName()} -- returns the common name of the SASL mechanism.</li>
+ * </ul>
+ * Subclasses will likely want to implement their own versions of these mthods:
+ * <li>{@link #authenticate(String, String, String)} -- Initiate authentication stanza using the
+ * deprecated method.</li>
+ * <li>{@link #authenticate(String, String, CallbackHandler)} -- Initiate authentication stanza
+ * using the CallbackHandler method.</li>
+ * <li>{@link #challengeReceived(String)} -- Handle a challenge from the server.</li>
+ * </ul>
+ *
+ * @author Jay Kline
+ */
+public abstract class SASLMechanism implements CallbackHandler {
+
+ private SASLAuthentication saslAuthentication;
+ protected SaslClient sc;
+ protected String authenticationId;
+ protected String password;
+ protected String hostname;
+
+
+ public SASLMechanism(SASLAuthentication saslAuthentication) {
+ this.saslAuthentication = saslAuthentication;
+ }
+
+ /**
+ * Builds and sends the <tt>auth</tt> 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 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, 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;
+ this.hostname = host;
+
+ String[] mechanisms = { getName() };
+ Map<String,String> props = new HashMap<String,String>();
+ sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
+ authenticate();
+ }
+
+ /**
+ * Builds and sends the <tt>auth</tt> 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<String,String> props = new HashMap<String,String>();
+ sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, cbh);
+ authenticate();
+ }
+
+ protected void authenticate() throws IOException, XMPPException {
+ String authenticationText = null;
+ try {
+ if(sc.hasInitialResponse()) {
+ byte[] response = sc.evaluateChallenge(new byte[0]);
+ authenticationText = StringUtils.encodeBase64(response, false);
+ }
+ } catch (SaslException e) {
+ throw new XMPPException("SASL authentication failed", e);
+ }
+
+ // Send the authentication to the server
+ getSASLAuthentication().send(new AuthMechanism(getName(), authenticationText));
+ }
+
+
+ /**
+ * The server is challenging the SASL mechanism for the stanza he just sent. Send a
+ * response to the server's challenge.
+ *
+ * @param challenge a base64 encoded string representing the challenge.
+ * @throws IOException if an exception sending the response occurs.
+ */
+ public void challengeReceived(String challenge) throws IOException {
+ byte response[];
+ if(challenge != null) {
+ response = sc.evaluateChallenge(StringUtils.decodeBase64(challenge));
+ } else {
+ response = sc.evaluateChallenge(new byte[0]);
+ }
+
+ Packet responseStanza;
+ if (response == null) {
+ responseStanza = new Response();
+ }
+ else {
+ responseStanza = new Response(StringUtils.encodeBase64(response, false));
+ }
+
+ // Send the authentication to the server
+ getSASLAuthentication().send(responseStanza);
+ }
+
+ /**
+ * 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();
+
+
+ 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) {
+ RealmCallback rcb = (RealmCallback)callbacks[i];
+ rcb.setText(hostname);
+ } else if(callbacks[i] instanceof RealmChoiceCallback){
+ //unused
+ //RealmChoiceCallback rccb = (RealmChoiceCallback)callbacks[i];
+ } else {
+ throw new UnsupportedCallbackException(callbacks[i]);
+ }
+ }
+ }
+
+ /**
+ * Initiating SASL authentication by select a mechanism.
+ */
+ public class AuthMechanism extends Packet {
+ final private String name;
+ final private String authenticationText;
+
+ public AuthMechanism(String name, String authenticationText) {
+ if (name == null) {
+ throw new NullPointerException("SASL mechanism name shouldn't be null.");
+ }
+ this.name = name;
+ this.authenticationText = authenticationText;
+ }
+
+ public String toXML() {
+ StringBuilder stanza = new StringBuilder();
+ stanza.append("<auth mechanism=\"").append(name);
+ stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ if (authenticationText != null &&
+ authenticationText.trim().length() > 0) {
+ stanza.append(authenticationText);
+ }
+ stanza.append("</auth>");
+ return stanza.toString();
+ }
+ }
+
+ /**
+ * A SASL challenge stanza.
+ */
+ public static class Challenge extends Packet {
+ final private String data;
+
+ public Challenge(String data) {
+ this.data = data;
+ }
+
+ public String toXML() {
+ StringBuilder stanza = new StringBuilder();
+ stanza.append("<challenge xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ if (data != null &&
+ data.trim().length() > 0) {
+ stanza.append(data);
+ }
+ stanza.append("</challenge>");
+ return stanza.toString();
+ }
+ }
+
+ /**
+ * A SASL response stanza.
+ */
+ public class Response extends Packet {
+ final private String authenticationText;
+
+ public Response() {
+ authenticationText = null;
+ }
+
+ public Response(String authenticationText) {
+ if (authenticationText == null || authenticationText.trim().length() == 0) {
+ this.authenticationText = null;
+ }
+ else {
+ this.authenticationText = authenticationText;
+ }
+ }
+
+ public String toXML() {
+ StringBuilder stanza = new StringBuilder();
+ stanza.append("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ if (authenticationText != null) {
+ stanza.append(authenticationText);
+ }
+ stanza.append("</response>");
+ return stanza.toString();
+ }
+ }
+
+ /**
+ * A SASL success stanza.
+ */
+ public static class Success extends Packet {
+ final private String data;
+
+ public Success(String data) {
+ this.data = data;
+ }
+
+ public String toXML() {
+ StringBuilder stanza = new StringBuilder();
+ stanza.append("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ if (data != null &&
+ data.trim().length() > 0) {
+ stanza.append(data);
+ }
+ stanza.append("</success>");
+ return stanza.toString();
+ }
+ }
+
+ /**
+ * A SASL failure stanza.
+ */
+ public static class Failure extends Packet {
+ final private String condition;
+
+ public Failure(String condition) {
+ this.condition = condition;
+ }
+
+ /**
+ * Get the SASL related error condition.
+ *
+ * @return the SASL related error condition.
+ */
+ public String getCondition() {
+ return condition;
+ }
+
+ public String toXML() {
+ StringBuilder stanza = new StringBuilder();
+ stanza.append("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
+ if (condition != null &&
+ condition.trim().length() > 0) {
+ stanza.append("<").append(condition).append("/>");
+ }
+ stanza.append("</failure>");
+ return stanza.toString();
+ }
+ }
+}
diff --git a/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java b/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java
new file mode 100644
index 0000000..cd973eb
--- /dev/null
+++ b/src/org/jivesoftware/smack/sasl/SASLPlainMechanism.java
@@ -0,0 +1,34 @@
+/**
+ *
+ * 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 PLAIN mechanism
+ *
+ * @author Jay Kline
+ */
+public class SASLPlainMechanism extends SASLMechanism {
+
+ public SASLPlainMechanism(SASLAuthentication saslAuthentication) {
+ super(saslAuthentication);
+ }
+
+ protected String getName() {
+ return "PLAIN";
+ }
+}
diff --git a/src/org/jivesoftware/smack/sasl/package.html b/src/org/jivesoftware/smack/sasl/package.html
new file mode 100644
index 0000000..1e8cfb7
--- /dev/null
+++ b/src/org/jivesoftware/smack/sasl/package.html
@@ -0,0 +1 @@
+<body>SASL Mechanisms.</body> \ No newline at end of file