blob: 78918077e886e81439ee2ab2a156e8841303a7bd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.directory.server.changepw;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.kerberos.KerberosPrincipal;
import net.sf.ehcache.Cache;
import org.apache.directory.server.changepw.protocol.ChangePasswordProtocolHandler;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.kerberos.shared.replay.ReplayCacheImpl;
import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
import org.apache.directory.server.kerberos.shared.store.DirectoryPrincipalStore;
import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
import org.apache.directory.server.protocol.shared.DirectoryBackedService;
import org.apache.directory.server.protocol.shared.transport.Transport;
import org.apache.directory.server.protocol.shared.transport.UdpTransport;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
import org.apache.directory.shared.ldap.name.DN;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.transport.socket.DatagramAcceptor;
import org.apache.mina.transport.socket.DatagramSessionConfig;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contains the configuration parameters for the Change Password protocol provider.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ChangePasswordServer extends DirectoryBackedService
{
private static final long serialVersionUID = 3509208713288140629L;
/** logger for this class */
private static final Logger LOG = LoggerFactory.getLogger( ChangePasswordServer.class.getName() );
/** The default change password principal name. */
private static final String SERVICE_PRINCIPAL_DEFAULT = "kadmin/changepw@EXAMPLE.COM";
/** The default change password realm. */
private static final String REALM_DEFAULT = "EXAMPLE.COM";
/** The default change password port. */
private static final int DEFAULT_IP_PORT = 464;
/** The default encryption types. */
public static final String[] ENCRYPTION_TYPES_DEFAULT = new String[]
{ "des-cbc-md5" };
/** The default changepw buffer size. */
private static final long DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * 60000;
/** The default empty addresses. */
private static final boolean DEFAULT_EMPTY_ADDRESSES_ALLOWED = true;
/** The default change password password policy for password length. */
public static final int DEFAULT_PASSWORD_LENGTH = 6;
/** The default change password password policy for category count. */
public static final int DEFAULT_CATEGORY_COUNT = 3;
/** The default change password password policy for token size. */
public static final int DEFAULT_TOKEN_SIZE = 3;
/** The default service PID. */
private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.changepw";
/** The default service name. */
private static final String SERVICE_NAME_DEFAULT = "ApacheDS Change Password Service";
/** The encryption types. */
private EncryptionType[] encryptionTypes;
/** The primary realm. */
private String primaryRealm = REALM_DEFAULT;
/** The service principal name. */
private String servicePrincipal = SERVICE_PRINCIPAL_DEFAULT;
/** The allowable clock skew. */
private long allowableClockSkew = DEFAULT_ALLOWABLE_CLOCKSKEW;
/** Whether empty addresses are allowed. */
private boolean isEmptyAddressesAllowed = DEFAULT_EMPTY_ADDRESSES_ALLOWED;
/** The policy for password length. */
private int policyPasswordLength;
/** The policy for category count. */
private int policyCategoryCount;
/** The policy for token size. */
private int policyTokenSize;
/** the cache used for storing change password requests */
private ReplayCache replayCache;
/**
* Creates a new instance of ChangePasswordConfiguration.
*/
public ChangePasswordServer()
{
super.setServiceName( SERVICE_NAME_DEFAULT );
super.setServiceId( SERVICE_PID_DEFAULT );
super.setSearchBaseDn( ServerDNConstants.USER_EXAMPLE_COM_DN );
prepareEncryptionTypes();
}
/**
* Returns the primary realm.
*
* @return The primary realm.
*/
public String getPrimaryRealm()
{
return primaryRealm;
}
/**
* @param primaryRealm The primaryRealm to set.
*/
public void setPrimaryRealm( String primaryRealm )
{
this.primaryRealm = primaryRealm;
}
/**
* Returns the encryption types.
*
* @return The encryption types.
*/
public EncryptionType[] getEncryptionTypes()
{
return encryptionTypes;
}
/**
* @param encryptionTypes The encryptionTypes to set.
*/
public void setEncryptionTypes( EncryptionType[] encryptionTypes )
{
this.encryptionTypes = encryptionTypes;
}
/**
* Returns the allowable clock skew.
*
* @return The allowable clock skew.
*/
public long getAllowableClockSkew()
{
return allowableClockSkew;
}
/**
* @param allowableClockSkew The allowableClockSkew to set.
*/
public void setAllowableClockSkew( long allowableClockSkew )
{
this.allowableClockSkew = allowableClockSkew;
}
/**
* Returns the Change Password service principal.
*
* @return The Change Password service principal.
*/
public KerberosPrincipal getServicePrincipal()
{
return new KerberosPrincipal( servicePrincipal );
}
/**
* @param servicePrincipal The Change Password service principal to set.
*/
public void setServicePrincipal( String servicePrincipal )
{
this.servicePrincipal = servicePrincipal;
}
/**
* Returns whether empty addresses are allowed.
*
* @return Whether empty addresses are allowed.
*/
public boolean isEmptyAddressesAllowed()
{
return isEmptyAddressesAllowed;
}
/**
* @param isEmptyAddressesAllowed The isEmptyAddressesAllowed to set.
*/
public void setEmptyAddressesAllowed( boolean isEmptyAddressesAllowed )
{
this.isEmptyAddressesAllowed = isEmptyAddressesAllowed;
}
/**
* Returns the password length.
*
* @return The password length.
*/
public int getPasswordLengthPolicy()
{
return policyPasswordLength;
}
/**
* Returns the category count.
*
* @return The category count.
*/
public int getCategoryCountPolicy()
{
return policyCategoryCount;
}
/**
* Returns the token size.
*
* @return The token size.
*/
public int getTokenSizePolicy()
{
return policyTokenSize;
}
/**
* @throws IOException if we cannot bind to the specified ports
*/
public void start() throws IOException, LdapInvalidDnException
{
PrincipalStore store = new DirectoryPrincipalStore( getDirectoryService(), new DN(this.getSearchBaseDn()) );
LOG.debug( "initializing the changepassword replay cache" );
Cache cache = getDirectoryService().getCacheService().getCache( "changePwdReplayCache" );
replayCache = new ReplayCacheImpl( cache );
if ( ( transports == null ) || ( transports.size() == 0 ) )
{
// Default to UDP with port 464
// We have to create a DatagramAcceptor
UdpTransport transport = new UdpTransport( DEFAULT_IP_PORT );
setTransports( transport );
DatagramAcceptor acceptor = (DatagramAcceptor)transport.getAcceptor();
// Set the handler
acceptor.setHandler( new ChangePasswordProtocolHandler( this, store ) );
// Allow the port to be reused even if the socket is in TIME_WAIT state
((DatagramSessionConfig)acceptor.getSessionConfig()).setReuseAddress( true );
// Start the listener
acceptor.bind();
}
else
{
for ( Transport transport:transports )
{
IoAcceptor acceptor = transport.getAcceptor();
// Disable the disconnection of the clients on unbind
acceptor.setCloseOnDeactivation( false );
if ( transport instanceof UdpTransport )
{
// Allow the port to be reused even if the socket is in TIME_WAIT state
((DatagramSessionConfig)acceptor.getSessionConfig()).setReuseAddress( true );
}
else
{
// Allow the port to be reused even if the socket is in TIME_WAIT state
((SocketAcceptor)acceptor).setReuseAddress( true );
// No Nagle's algorithm
((SocketAcceptor)acceptor).getSessionConfig().setTcpNoDelay( true );
}
// Set the handler
acceptor.setHandler( new ChangePasswordProtocolHandler( this, store ) );
// Bind
acceptor.bind();
}
}
LOG.info( "ChangePassword service started." );
//System.out.println( "ChangePassword service started." );
}
public void stop()
{
for ( Transport transport :getTransports() )
{
IoAcceptor acceptor = transport.getAcceptor();
if ( acceptor != null )
{
acceptor.dispose();
}
}
replayCache.clear();
LOG.info( "ChangePassword service stopped." );
//System.out.println( "ChangePassword service stopped." );
}
private void prepareEncryptionTypes()
{
String[] encryptionTypeStrings = ENCRYPTION_TYPES_DEFAULT;
List<EncryptionType> encTypes = new ArrayList<EncryptionType>();
for ( String enc : encryptionTypeStrings )
{
for ( EncryptionType type : EncryptionType.getEncryptionTypes() )
{
if ( type.toString().equalsIgnoreCase( enc ) )
{
encTypes.add( type );
}
}
}
encryptionTypes = encTypes.toArray( new EncryptionType[encTypes.size()] );
}
/**
* Sets the policy's minimum?? password length.
*
* @param policyPasswordLength the minimum password length requirement
*/
public void setPolicyPasswordLength( int policyPasswordLength )
{
this.policyPasswordLength = policyPasswordLength;
}
/**
* Sets the policy category count - what's this?
*
* @param policyCategoryCount the policy category count
*/
public void setPolicyCategoryCount( int policyCategoryCount )
{
this.policyCategoryCount = policyCategoryCount;
}
/**
* Sets the policy token size - what's this?
*
* @param policyTokenSize the policy token size
*/
public void setPolicyTokenSize( int policyTokenSize )
{
this.policyTokenSize = policyTokenSize;
}
/**
* @return the replayCache
*/
public ReplayCache getReplayCache()
{
return replayCache;
}
/**
* @see Object#toString()
*/
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "ChangePasswordServer[" ).append( getServiceName() ).append( "], listening on :" ).append( '\n' );
if ( getTransports() != null )
{
for ( Transport transport:getTransports() )
{
sb.append( " " ).append( transport ).append( '\n' );
}
}
return sb.toString();
}
}