Commit 8980337d authored by Guus der Kinderen's avatar Guus der Kinderen

Merge pull request #554 from guusdk/OF-1097

OF-1097: jabber:iq:auth / XEP-0078 disabled by default
parents a0cbf5f4 2cd0137f
......@@ -444,6 +444,16 @@ public class IQRouter extends BasicModule {
routingTable.routePacket(reply.getTo(), reply, true);
}
/**
* Determines if this instance has support (formally: has a IQ Handler) for the provided namespace.
*
* @param namespace Identifier of functionality (cannot be null)
* @return true if the functionality identified by the namespace is supported, otherwise false.
*/
public boolean supports( String namespace ) {
return getHandler( namespace ) != null;
}
private IQHandler getHandler(String namespace) {
IQHandler handler = namespace2Handlers.get(namespace);
if (handler == null) {
......
......@@ -504,7 +504,6 @@ public class XMPPServer {
// Load standard modules
loadModule(IQBindHandler.class.getName());
loadModule(IQSessionEstablishmentHandler.class.getName());
loadModule(IQAuthHandler.class.getName());
loadModule(IQPingHandler.class.getName());
loadModule(IQPrivateHandler.class.getName());
loadModule(IQRegisterHandler.class.getName());
......@@ -1062,17 +1061,6 @@ public class XMPPServer {
return (IQRegisterHandler) modules.get(IQRegisterHandler.class);
}
/**
* Returns the <code>IQAuthHandler</code> registered with this server. The
* <code>IQAuthHandler</code> was registered with the server as a module while starting up
* the server.
*
* @return the <code>IQAuthHandler</code> registered with this server.
*/
public IQAuthHandler getIQAuthHandler() {
return (IQAuthHandler) modules.get(IQAuthHandler.class);
}
/**
* Returns the <code>IQPEPHandler</code> registered with this server. The
* <code>IQPEPHandler</code> was registered with the server as a module while starting up
......
......@@ -24,6 +24,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.Blowfish;
......@@ -146,27 +147,6 @@ public class AuthFactory {
return authProvider.supportsPasswordRetrieval();
}
/**
* Returns true if the currently installed {@link AuthProvider} supports authentication
* using plain-text passwords according to JEP-0078. Plain-text authentication is
* not secure and should generally only be used over a TLS/SSL connection.
*
* @return true if plain text password authentication is supported.
*/
public static boolean isPlainSupported() {
return authProvider.isPlainSupported();
}
/**
* Returns true if the currently installed {@link AuthProvider} supports
* digest authentication according to JEP-0078.
*
* @return true if digest authentication is supported.
*/
public static boolean isDigestSupported() {
return authProvider.isDigestSupported();
}
/**
* Returns the user's password. This method will throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
......@@ -218,30 +198,6 @@ public class AuthFactory {
return new AuthToken(username);
}
/**
* Authenticates a user with a username, token, and digest and returns an AuthToken.
* The digest should be generated using the {@link #createDigest(String, String)} method.
* If the username and digest do not match the record of any user in the system, the
* method throws an UnauthorizedException.
*
* @param username the username.
* @param token the token that was used with plain-text password to generate the digest.
* @param digest the digest generated from plain-text password and unique token.
* @return an AuthToken token if the username and digest are correct for the user's
* password and given token.
* @throws UnauthorizedException if the username and password do not match any
* existing user or the account is locked out.
*/
public static AuthToken authenticate(String username, String token, String digest)
throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
if (LockOutManager.getInstance().isAccountDisabled(username)) {
LockOutManager.getInstance().recordFailedLogin(username);
throw new UnauthorizedException();
}
authProvider.authenticate(username, token, digest);
return new AuthToken(username);
}
/**
* Returns a digest given a token and password, according to JEP-0078.
*
......
......@@ -39,32 +39,10 @@ import org.jivesoftware.openfire.user.UserNotFoundException;
*/
public interface AuthProvider {
/**
* Returns true if this AuthProvider supports authentication using plain-text
* passwords according to JEP--0078. Plain text authentication is not secure
* and should generally only be used for a TLS/SSL connection.
*
* @return true if plain text password authentication is supported by
* this AuthProvider.
*/
boolean isPlainSupported();
/**
* Returns true if this AuthProvider supports digest authentication
* according to JEP-0078.
*
* @return true if digest authentication is supported by this
* AuthProvider.
*/
boolean isDigestSupported();
/**
* Returns if the username and password are valid; otherwise this
* method throws an UnauthorizedException.<p>
*
* If {@link #isPlainSupported()} returns false, this method should
* throw an UnsupportedOperationException.
*
* @param username the username or full JID.
* @param password the password
* @throws UnauthorizedException if the username and password do
......@@ -75,25 +53,6 @@ public interface AuthProvider {
void authenticate(String username, String password) throws UnauthorizedException,
ConnectionException, InternalUnauthenticatedException;
/**
* Returns if the username, token, and digest are valid; otherwise this
* method throws an UnauthorizedException.<p>
*
* If {@link #isDigestSupported()} returns false, this method should
* throw an UnsupportedOperationException.
*
* @param username the username or full JID.
* @param token the token that was used with plain-text password to
* generate the digest.
* @param digest the digest generated from plain-text password and unique token.
* @throws UnauthorizedException if the username and password
* do not match any existing user.
* @throws ConnectionException it there is a problem connecting to user and group sytem
* @throws InternalUnauthenticatedException if there is a problem authentication Openfire iteself into the user and group system
*/
void authenticate(String username, String token, String digest)
throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException;
/**
* Returns the user's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
......
......@@ -96,47 +96,6 @@ public class DefaultAuthProvider implements AuthProvider {
// Got this far, so the user must be authorized.
}
@Override
public void authenticate(String username, String token, String digest) throws UnauthorizedException {
if (username == null || token == null || digest == null) {
throw new UnauthorizedException();
}
username = username.trim().toLowerCase();
if (username.contains("@")) {
// Check that the specified domain matches the server's domain
int index = username.indexOf("@");
String domain = username.substring(index + 1);
if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
username = username.substring(0, index);
} else {
// Unknown domain. Return authentication failed.
throw new UnauthorizedException();
}
}
try {
String password = getPassword(username);
String anticipatedDigest = AuthFactory.createDigest(token, password);
if (!digest.equalsIgnoreCase(anticipatedDigest)) {
throw new UnauthorizedException();
}
}
catch (UserNotFoundException unfe) {
throw new UnauthorizedException();
}
// Got this far, so the user must be authorized.
}
@Override
public boolean isPlainSupported() {
return true;
}
@Override
public boolean isDigestSupported() {
boolean scramOnly = JiveGlobals.getBooleanProperty("user.scramHashedPasswordOnly");
return !scramOnly;
}
@Override
public String getPassword(String username) throws UserNotFoundException {
if (!supportsPasswordRetrieval()) {
......
......@@ -106,13 +106,6 @@ public class HybridAuthProvider implements AuthProvider {
try {
Class c = ClassUtils.forName(primaryClass);
primaryProvider = (AuthProvider)c.newInstance();
// All providers must support plain auth.
if (!primaryProvider.isPlainSupported()) {
Log.error("Provider " + primaryClass + " must support plain authentication. " +
"Authentication disabled.");
primaryProvider = null;
return;
}
Log.debug("Primary auth provider: " + primaryClass);
}
catch (Exception e) {
......@@ -127,14 +120,6 @@ public class HybridAuthProvider implements AuthProvider {
try {
Class c = ClassUtils.forName(secondaryClass);
secondaryProvider = (AuthProvider)c.newInstance();
// All providers must support plain auth.
if (!secondaryProvider.isPlainSupported()) {
Log.error("Provider " + secondaryClass + " must support plain authentication. " +
"Authentication disabled.");
primaryProvider = null;
secondaryProvider = null;
return;
}
Log.debug("Secondary auth provider: " + secondaryClass);
}
catch (Exception e) {
......@@ -148,15 +133,6 @@ public class HybridAuthProvider implements AuthProvider {
try {
Class c = ClassUtils.forName(tertiaryClass);
tertiaryProvider = (AuthProvider)c.newInstance();
// All providers must support plain auth.
if (!tertiaryProvider.isPlainSupported()) {
Log.error("Provider " + tertiaryClass + " must support plain authentication. " +
"Authentication disabled.");
primaryProvider = null;
secondaryProvider = null;
tertiaryProvider = null;
return;
}
Log.debug("Tertiary auth provider: " + tertiaryClass);
}
catch (Exception e) {
......@@ -188,16 +164,6 @@ public class HybridAuthProvider implements AuthProvider {
}
}
@Override
public boolean isPlainSupported() {
return true;
}
@Override
public boolean isDigestSupported() {
return false;
}
@Override
public void authenticate(String username, String password) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
// Check overrides first.
......@@ -238,13 +204,6 @@ public class HybridAuthProvider implements AuthProvider {
}
}
@Override
public void authenticate(String username, String token, String digest)
throws UnauthorizedException
{
throw new UnauthorizedException("Digest authentication not supported.");
}
@Override
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
......
......@@ -258,57 +258,6 @@ public class JDBCAuthProvider implements AuthProvider, PropertyEventListener {
}
}
@Override
public void authenticate(String username, String token, String digest)
throws UnauthorizedException
{
if (passwordTypes.size() != 1 || passwordTypes.get(0) != PasswordType.plain) {
throw new UnsupportedOperationException("Digest authentication not supported for "
+ "password type " + passwordTypes.get(0));
}
if (username == null || token == null || digest == null) {
throw new UnauthorizedException();
}
username = username.trim().toLowerCase();
if (username.contains("@")) {
// Check that the specified domain matches the server's domain
int index = username.indexOf("@");
String domain = username.substring(index + 1);
if (domain.equals(XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
username = username.substring(0, index);
} else {
// Unknown domain. Return authentication failed.
throw new UnauthorizedException();
}
}
String password;
try {
password = getPasswordValue(username);
}
catch (UserNotFoundException unfe) {
throw new UnauthorizedException();
}
String anticipatedDigest = AuthFactory.createDigest(token, password);
if (!digest.equalsIgnoreCase(anticipatedDigest)) {
throw new UnauthorizedException();
}
// Got this far, so the user must be authorized.
createUser(username);
}
@Override
public boolean isPlainSupported() {
// If the auth SQL is defined, plain text authentication is supported.
return (passwordSQL != null);
}
@Override
public boolean isDigestSupported() {
// The auth SQL must be defined and the password type is supported.
return (passwordSQL != null && passwordTypes.size() == 1 && passwordTypes.get(0) == PasswordType.plain);
}
@Override
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException
......
......@@ -182,23 +182,6 @@ public class NativeAuthProvider implements AuthProvider {
}
}
@Override
public void authenticate(String username, String token, String digest)
throws UnauthorizedException
{
throw new UnsupportedOperationException();
}
@Override
public boolean isPlainSupported() {
return true;
}
@Override
public boolean isDigestSupported() {
return false;
}
@Override
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
......
......@@ -219,23 +219,6 @@ public class POP3AuthProvider implements AuthProvider {
}
}
@Override
public void authenticate(String username, String token, String digest)
throws UnauthorizedException
{
throw new UnauthorizedException("Digest authentication not supported.");
}
@Override
public boolean isPlainSupported() {
return true;
}
@Override
public boolean isDigestSupported() {
return false;
}
@Override
public String getPassword(String username)
throws UserNotFoundException, UnsupportedOperationException
......
......@@ -43,16 +43,6 @@ public class CrowdAuthProvider implements AuthProvider {
}
}
@Override
public boolean isPlainSupported() {
return true;
}
@Override
public boolean isDigestSupported() {
return false;
}
/**
* Returns if the username and password are valid; otherwise this
* method throws an UnauthorizedException.<p>
......@@ -96,11 +86,6 @@ public class CrowdAuthProvider implements AuthProvider {
}
}
@Override
public void authenticate(String username, String token, String digest) throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
throw new UnsupportedOperationException("XMPP digest authentication not supported by this version of authentication provider");
}
@Override
public String getPassword(String username) throws UserNotFoundException, UnsupportedOperationException {
throw new UnsupportedOperationException("Retrieve password not supported by this version of authentication provider");
......
/**
* $RCSfile$
* $Revision: 128 $
* $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
*
* Copyright (C) 2004-2008 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.openfire.handler;
import org.jivesoftware.openfire.auth.UnauthorizedException;
/**
* Information for controlling the authentication options for the server.
*
* @author Iain Shigeoka
*/
public interface IQAuthInfo {
/**
* Returns true if anonymous authentication is allowed.
*
* @return true if anonymous logins are allowed
*/
public boolean isAnonymousAllowed();
/**
* Changes the server's support for anonymous authentication.
*
* @param isAnonymous True if anonymous logins should be allowed.
* @throws UnauthorizedException If you don't have permission to adjust this setting
*/
public void setAllowAnonymous(boolean isAnonymous) throws UnauthorizedException;
}
\ No newline at end of file
......@@ -71,16 +71,6 @@ public class LdapAuthProvider implements AuthProvider {
}
}
@Override
public boolean isPlainSupported() {
return true;
}
@Override
public boolean isDigestSupported() {
return false;
}
@Override
public void authenticate(String username, String password) throws UnauthorizedException {
if (username == null || password == null || "".equals(password.trim())) {
......@@ -146,11 +136,6 @@ public class LdapAuthProvider implements AuthProvider {
}
}
@Override
public void authenticate(String username, String token, String digest) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Digest authentication not currently supported.");
}
@Override
public String getPassword(String username) throws UserNotFoundException,
UnsupportedOperationException
......
......@@ -127,7 +127,7 @@ public class SASLAuthentication {
/**
* SASL negotiation has been successful.
*/
authenticated;
authenticated
}
/**
......@@ -230,7 +230,7 @@ public class SASLAuthentication {
// Construct the configuration properties
final Map<String, Object> props = new HashMap<>();
props.put( LocalClientSession.class.getCanonicalName(), session );
props.put( Sasl.POLICY_NOANONYMOUS, Boolean.toString( !XMPPServer.getInstance().getIQAuthHandler().isAnonymousAllowed() ) );
props.put( Sasl.POLICY_NOANONYMOUS, Boolean.toString( !JiveGlobals.getBooleanProperty( "xmpp.auth.anonymous" ) ) );
SaslServer saslServer = Sasl.createSaslServer( mechanismName, "xmpp", session.getServerName(), props, new XMPPCallbackHandler() );
if ( saslServer == null )
......@@ -437,7 +437,7 @@ public class SASLAuthentication {
* mechanism by Openfire. Actual SASL handling is done by Java itself, so you must add
* the provider to Java.
*
* @param mechanism the name of the new SASL mechanism (cannot be null or an empty String).
* @param mechanismName the name of the new SASL mechanism (cannot be null or an empty String).
*/
public static void addSupportedMechanism(String mechanismName) {
if ( mechanismName == null || mechanismName.isEmpty() ) {
......@@ -490,7 +490,7 @@ public class SASLAuthentication {
}
else if (mech.equals("ANONYMOUS")) {
// Check anonymous is supported
if (!XMPPServer.getInstance().getIQAuthHandler().isAnonymousAllowed()) {
if (!JiveGlobals.getBooleanProperty( "xmpp.auth.anonymous" )) {
it.remove();
}
}
......
......@@ -3,6 +3,7 @@ package org.jivesoftware.openfire.sasl;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.openfire.session.LocalSession;
import org.jivesoftware.util.JiveGlobals;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslException;
......@@ -45,7 +46,7 @@ public class AnonymousSaslServer implements SaslServer
complete = true;
// Verify server-wide policy.
if ( !XMPPServer.getInstance().getIQAuthHandler().isAnonymousAllowed() )
if ( !JiveGlobals.getBooleanProperty( "xmpp.auth.anonymous" ) )
{
throw new SaslException( "Authentication failed" );
}
......
......@@ -883,7 +883,9 @@ public class LocalClientSession extends LocalSession implements ClientSession {
if (getAuthToken() == null) {
// Advertise that the server supports Non-SASL Authentication
if ( XMPPServer.getInstance().getIQRouter().supports( "jabber:iq:auth" ) ) {
sb.append("<auth xmlns=\"http://jabber.org/features/iq-auth\"/>");
}
// Advertise that the server supports In-Band Registration
if (XMPPServer.getInstance().getIQRegisterHandler().isInbandRegEnabled()) {
sb.append("<register xmlns=\"http://jabber.org/features/iq-register\"/>");
......
......@@ -279,7 +279,9 @@ public class LocalConnectionMultiplexerSession extends LocalSession implements C
comp.addElement("method").setText("zlib");
}
// Add info about Non-SASL authentication
if (XMPPServer.getInstance().getIQRouter().supports("jabber:iq:auth")) {
child.addElement("auth", "http://jabber.org/features/iq-auth");
}
// Add info about In-Band Registration
if (XMPPServer.getInstance().getIQRegisterHandler().isInbandRegEnabled()) {
child.addElement("register", "http://jabber.org/features/iq-register");
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Non-SASL Authentication Plugin Changelog</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
padding-left : 1em;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
</style>
</head>
<body>
<h1>
Non-SASL Authentication Plugin Changelog
</h1>
<p><b>1.0.0</b> -- March 3, 2016</p>
<ul>
<li>[<a href='http://www.igniterealtime.org/issues/browse/'></a>] - Initial release (moved code from Openfire core to new plugin).</li>
</ul>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>org.jivesoftware.openfire.plugin.NonSaslAuthenticationPlugin</class>
<name>Non-SASL Authentication</name>
<description>This plugin implements a the (obsolete!) XEP-0078 specification for authentication using the jabber:iq:auth namespace.</description>
<author>Guus der Kinderen</author>
<version>1.0.0</version>
<date>3/3/2016</date>
<minServerVersion>4.1.0 Alpha</minServerVersion>
</plugin>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Non-SASL Authentication Plugin Readme</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
}
H2 {
font-size : 10pt;
font-weight : bold;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
</style>
</head>
<body>
<h1>
Non-SASL Authentication Plugin Readme
</h1>
<h2>Overview</h2>
<p>
The Non-SASL Authentication plugin provides a an implementation for authentication with Jabber servers and
services using the jabber:iq:auth namespace.simple, as specified in
<a href="http://xmpp.org/extensions/xep-0078.html">XEP-0078: Non-SASL Authentication</a>.
</p>
<p>
Note Well: The protocol implemented by this plugin has been superseded in favor of SASL authentication as specified
by the XMPP standards in RFC 3920 / RFC 6120, and is now obsolete. This plugin should not be installed in Openfire,
unless there is a pressing need for backwards compatibility with regards to XEP-0078.
</p>
<p>
In versions of Openfire prior to (and excluding) 4.1.0 the functionality provided in this plugin was part of the
base functionality of Openfire itself. Years after SASL was introduced as the preferred method of authentication in
XMPP (and the corresponding formal obsoletion of non-SASL authentication in 2008), support for Non-SASL
Authentication was dropped from Openfire in version 4.1.0. For environments where backwards compatibility is
required, this plugin can be used to restore Non-SASL Authentication functionality in Openfire.
</p>
<h2>Installation</h2>
<p>
Copy nonSaslAuthentication.jar into the plugins directory of your Openfire installation. The plugin will then be
automatically deployed. To upgrade to a new version, copy the new nonSaslAuthentication.jar file over the existing
file.
</p>
</body>
</html>
......@@ -23,7 +23,6 @@ package org.jivesoftware.openfire.handler;
import gnu.inet.encoding.Stringprep;
import gnu.inet.encoding.StringprepException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
......@@ -41,6 +40,7 @@ import org.jivesoftware.openfire.auth.ConnectionException;
import org.jivesoftware.openfire.auth.InternalUnauthenticatedException;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.event.SessionEventDispatcher;
import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.openfire.session.LocalClientSession;
import org.jivesoftware.openfire.session.Session;
......@@ -74,12 +74,10 @@ import org.xmpp.packet.StreamError;
*
* @author Iain Shigeoka
*/
public class IQAuthHandler extends IQHandler implements IQAuthInfo {
public class IQAuthHandler extends IQHandler {
private static final Logger Log = LoggerFactory.getLogger(IQAuthHandler.class);
private boolean anonymousAllowed;
private Element probeResponse;
private IQHandlerInfo info;
......@@ -97,14 +95,11 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
probeResponse = DocumentHelper.createElement(QName.get("query", "jabber:iq:auth"));
probeResponse.addElement("username");
if (AuthFactory.isPlainSupported()) {
if (AuthFactory.supportsPasswordRetrieval()) {
probeResponse.addElement("password");
}
if (AuthFactory.isDigestSupported()) {
probeResponse.addElement("digest");
}
probeResponse.addElement("resource");
anonymousAllowed = JiveGlobals.getBooleanProperty("xmpp.auth.anonymous");
}
@Override
......@@ -254,12 +249,12 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
username = username.toLowerCase();
// Verify that supplied username and password are correct (i.e. user authentication was successful)
AuthToken token = null;
if (password != null && AuthFactory.isPlainSupported()) {
token = AuthFactory.authenticate(username, password);
if ( AuthFactory.supportsPasswordRetrieval() ) {
if ( password != null) {
token = AuthFactory.authenticate( username, password );
} else if ( digest != null) {
token = authenticate(username, session.getStreamID().toString(), digest );
}
else if (digest != null && AuthFactory.isDigestSupported()) {
token = AuthFactory.authenticate(username, session.getStreamID().toString(),
digest);
}
if (token == null) {
throw new UnauthorizedException();
......@@ -328,7 +323,7 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
private IQ anonymousLogin(LocalClientSession session, IQ packet) {
IQ response = IQ.createResultIQ(packet);
if (anonymousAllowed) {
if (JiveGlobals.getBooleanProperty("xmpp.auth.anonymous")) {
// Verify that client can connect from his IP address
boolean forbidAccess = !LocalClientSession.isAllowedAnonymous( session.getConnection() );
if (forbidAccess) {
......@@ -352,17 +347,6 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
return response;
}
@Override
public boolean isAnonymousAllowed() {
return anonymousAllowed;
}
@Override
public void setAllowAnonymous(boolean isAnonymous) throws UnauthorizedException {
anonymousAllowed = isAnonymous;
JiveGlobals.setProperty("xmpp.auth.anonymous", Boolean.toString(anonymousAllowed));
}
@Override
public void initialize(XMPPServer server) {
super.initialize(server);
......@@ -376,4 +360,54 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
public IQHandlerInfo getInfo() {
return info;
}
/**
* Authenticates a user with a username, token, and digest and returns an AuthToken.
* The digest should be generated using the {@link AuthFactory#createDigest(String, String)} method.
* If the username and digest do not match the record of any user in the system, the
* method throws an UnauthorizedException.
*
* @param username the username.
* @param token the token that was used with plain-text password to generate the digest.
* @param digest the digest generated from plain-text password and unique token.
* @return an AuthToken token if the username and digest are correct for the user's
* password and given token.
* @throws UnauthorizedException if the username and password do not match any
* existing user or the account is locked out.
*/
public static AuthToken authenticate(String username, String token, String digest)
throws UnauthorizedException, ConnectionException, InternalUnauthenticatedException {
if (username == null || token == null || digest == null) {
throw new UnauthorizedException();
}
if ( LockOutManager.getInstance().isAccountDisabled(username)) {
LockOutManager.getInstance().recordFailedLogin(username);
throw new UnauthorizedException();
}
username = username.trim().toLowerCase();
if (username.contains("@")) {
// Check that the specified domain matches the server's domain
int index = username.indexOf("@");
String domain = username.substring(index + 1);
if (domain.equals( XMPPServer.getInstance().getServerInfo().getXMPPDomain())) {
username = username.substring(0, index);
} else {
// Unknown domain. Return authentication failed.
throw new UnauthorizedException();
}
}
try {
String password = AuthFactory.getPassword( username );
String anticipatedDigest = AuthFactory.createDigest(token, password);
if (!digest.equalsIgnoreCase(anticipatedDigest)) {
throw new UnauthorizedException();
}
}
catch (UserNotFoundException unfe) {
throw new UnauthorizedException();
}
// Got this far, so the user must be authorized.
return new AuthToken(username);
}
}
package org.jivesoftware.openfire.plugin;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.handler.IQAuthHandler;
import java.io.File;
/**
* An Openfire plugin that implements the obsolete Non-SASL Authentication plugin as specified in XEP-0078.
*
* @author Guus der Kinderen, guus@goodbytes.nl
* @see <a href="http://xmpp.org/extensions/xep-0078.html">XEP-0078: Non-SASL Authentication</a>
*/
public class NonSaslAuthenticationPlugin implements Plugin
{
private IQAuthHandler iqAuthHandler;
@Override
public void initializePlugin( PluginManager manager, File pluginDirectory )
{
iqAuthHandler = new IQAuthHandler();
XMPPServer.getInstance().getIQRouter().addHandler( iqAuthHandler );
}
@Override
public void destroyPlugin()
{
if ( iqAuthHandler != null )
{
XMPPServer.getInstance().getIQRouter().removeHandler( iqAuthHandler );
iqAuthHandler = null;
}
}
}
\ No newline at end of file
......@@ -44,6 +44,13 @@
Openfire WebSocket Plugin Changelog
</h1>
<p><b>1.1.4</b> -- March 3, 2016</p>
<ul>
<li>[<a href='https://igniterealtime.org/issues/browse/OF-1097'></a>] - Non-SASL Authentication support should be optional.</li>
<li>Minimum server requirement: 4.1.0 Alpha</li>
</ul>
<p><b>1.1.3</b> -- January 6, 2016</p>
<ul>
......
......@@ -8,8 +8,8 @@
<name>Openfire WebSocket</name>
<description>Provides WebSocket support for Openfire.</description>
<author>Tom Evans</author>
<version>1.1.3</version>
<date>01/06/2016</date>
<version>1.1.4</version>
<date>03/03/2016</date>
<url>https://tools.ietf.org/html/rfc7395</url>
<minServerVersion>4.0.0 Alpha</minServerVersion>
<minServerVersion>4.1.0 Alpha</minServerVersion>
</plugin>
\ No newline at end of file
......@@ -298,7 +298,9 @@ public class XmppWebSocket {
if (saslStatus == null) {
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(xmppSession));
if (XMPPServer.getInstance().getIQRouter().supports("jabber:iq:auth")) {
sb.append("<auth xmlns='http://jabber.org/features/iq-auth'/>");
}
} else if (saslStatus.equals(Status.authenticated)) {
// Include Stream features
sb.append(String.format("<bind xmlns='%s'/>", "urn:ietf:params:xml:ns:xmpp-bind"));
......
......@@ -18,7 +18,6 @@
--%>
<%@ page import="org.jivesoftware.openfire.XMPPServer,
org.jivesoftware.openfire.handler.IQAuthHandler,
org.jivesoftware.openfire.handler.IQRegisterHandler,
org.jivesoftware.openfire.session.LocalClientSession,
org.jivesoftware.util.ParamUtils"
......@@ -26,6 +25,7 @@
%>
<%@ page import="java.util.regex.Pattern" %>
<%@ page import="java.util.*" %>
<%@ page import="org.jivesoftware.util.JiveGlobals" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
......@@ -51,12 +51,11 @@
String blockedIPs = request.getParameter("blockedIPs");
// Get an IQRegisterHandler:
IQRegisterHandler regHandler = XMPPServer.getInstance().getIQRegisterHandler();
IQAuthHandler authHandler = XMPPServer.getInstance().getIQAuthHandler();
if (save) {
regHandler.setInbandRegEnabled(inbandEnabled);
regHandler.setCanChangePassword(canChangePassword);
authHandler.setAllowAnonymous(anonLogin);
JiveGlobals.setProperty("xmpp.auth.anonymous", Boolean.toString(anonLogin));
// Build a Map with the allowed IP addresses
Pattern pattern = Pattern.compile("(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.)" +
......@@ -100,7 +99,7 @@
// Reset the value of page vars:
inbandEnabled = regHandler.isInbandRegEnabled();
canChangePassword = regHandler.canChangePassword();
anonLogin = authHandler.isAnonymousAllowed();
anonLogin = JiveGlobals.getBooleanProperty( "xmpp.auth.anonymous" );
// Encode the allowed IP addresses
StringBuilder buf = new StringBuilder();
Iterator<String> iter = org.jivesoftware.openfire.session.LocalClientSession.getWhitelistedIPs().iterator();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment