/** * * Copyright 2003-2007 Jive Software. * * 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; import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.Mechanisms; import org.jivesoftware.smack.sasl.SASLErrorException; import org.jivesoftware.smack.sasl.SASLMechanism; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure; import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityBareJid; import javax.security.auth.callback.CallbackHandler; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; /** *
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 * register with the server or authenticate using SASL. If the * server supports SASL then Smack will try to authenticate using SASL..
* *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(SASLMechanism)} to register a new mechanisms.
*
* @see org.jivesoftware.smack.sasl.SASLMechanism
*
* @author Gaston Dombiak
* @author Jay Kline
*/
public final class SASLAuthentication {
private static final Logger LOGGER = Logger.getLogger(SASLAuthentication.class.getName());
private static final List
*
* 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 password the password to send to the server.
* @param authzid the authorization identifier (typically null).
* @throws XMPPErrorException
* @throws SASLErrorException
* @throws IOException
* @throws SmackException
* @throws InterruptedException
*/
public void authenticate(String username, String password, EntityBareJid authzid)
throws XMPPErrorException, SASLErrorException, IOException,
SmackException, InterruptedException {
currentMechanism = selectMechanism(authzid);
final CallbackHandler callbackHandler = configuration.getCallbackHandler();
final String host = connection.getHost();
final DomainBareJid xmppServiceDomain = connection.getXMPPServiceDomain();
synchronized (this) {
if (callbackHandler != null) {
currentMechanism.authenticate(host, xmppServiceDomain, callbackHandler, authzid);
}
else {
currentMechanism.authenticate(username, host, xmppServiceDomain, password, authzid);
}
final long deadline = System.currentTimeMillis() + connection.getPacketReplyTimeout();
while (!authenticationSuccessful && saslException == null) {
final long now = System.currentTimeMillis();
if (now > deadline) break;
// Wait until SASL negotiation finishes
wait(deadline - now);
}
}
if (saslException != null){
if (saslException instanceof SmackException) {
throw (SmackException) saslException;
} else if (saslException instanceof SASLErrorException) {
throw (SASLErrorException) saslException;
} else {
throw new IllegalStateException("Unexpected exception type" , saslException);
}
}
if (!authenticationSuccessful) {
throw NoResponseException.newWith(connection, "successful SASL authentication");
}
}
/**
* Wrapper for {@link #challengeReceived(String, boolean)}, with finalChallenge
set
* to false
.
*
* @param challenge a base64 encoded string representing the challenge.
* @throws SmackException
* @throws InterruptedException
*/
public void challengeReceived(String challenge) throws SmackException, InterruptedException {
challengeReceived(challenge, false);
}
/**
* The server is challenging the SASL authentication we just sent. Forward the challenge
* to the current SASLMechanism we are using. The SASLMechanism will eventually send a response to
* the server. The length of the challenge-response sequence varies according to the
* SASLMechanism in use.
*
* @param challenge a base64 encoded string representing the challenge.
* @param finalChallenge true if this is the last challenge send by the server within the success stanza
* @throws SmackException
* @throws InterruptedException
*/
public void challengeReceived(String challenge, boolean finalChallenge) throws SmackException, InterruptedException {
try {
currentMechanism.challengeReceived(challenge, finalChallenge);
} catch (InterruptedException | SmackException e) {
authenticationFailed(e);
throw e;
}
}
/**
* Notification message saying that SASL authentication was successful. The next step
* would be to bind the resource.
* @throws SmackException
* @throws InterruptedException
*/
public void authenticated(Success success) throws SmackException, InterruptedException {
// RFC6120 6.3.10 "At the end of the authentication exchange, the SASL server (the XMPP
// "receiving entity") can include "additional data with success" if appropriate for the
// SASL mechanism in use. In XMPP, this is done by including the additional data as the XML
// character data of the