001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.changepw;
021    
022    
023    import java.io.IOException;
024    import java.util.ArrayList;
025    import java.util.List;
026    
027    import javax.security.auth.kerberos.KerberosPrincipal;
028    
029    import org.apache.directory.server.changepw.protocol.ChangePasswordProtocolHandler;
030    import org.apache.directory.server.constants.ServerDNConstants;
031    import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
032    import org.apache.directory.server.kerberos.shared.store.DirectoryPrincipalStore;
033    import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
034    import org.apache.directory.server.protocol.shared.DirectoryBackedService;
035    import org.apache.directory.server.protocol.shared.transport.TcpTransport;
036    import org.apache.directory.server.protocol.shared.transport.Transport;
037    import org.apache.directory.server.protocol.shared.transport.UdpTransport;
038    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
039    import org.apache.directory.shared.ldap.name.DN;
040    import org.apache.mina.core.service.IoAcceptor;
041    import org.apache.mina.transport.socket.DatagramAcceptor;
042    import org.apache.mina.transport.socket.DatagramSessionConfig;
043    import org.apache.mina.transport.socket.SocketAcceptor;
044    import org.slf4j.Logger;
045    import org.slf4j.LoggerFactory;
046    
047    
048    /**
049     * Contains the configuration parameters for the Change Password protocol provider.
050     *
051     * @org.apache.xbean.XBean
052     *
053     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054     * @version $Rev: 923757 $, $Date: 2010-03-16 15:33:59 +0100 (Tue, 16 Mar 2010) $
055     */
056    public class ChangePasswordServer extends DirectoryBackedService
057    {
058        private static final long serialVersionUID = 3509208713288140629L;
059    
060        /** logger for this class */
061        private static final Logger LOG = LoggerFactory.getLogger( ChangePasswordServer.class.getName() );
062    
063        /** The default change password principal name. */
064        private static final String SERVICE_PRINCIPAL_DEFAULT = "kadmin/changepw@EXAMPLE.COM";
065    
066        /** The default change password realm. */
067        private static final String REALM_DEFAULT = "EXAMPLE.COM";
068    
069        /** The default change password port. */
070        private static final int DEFAULT_IP_PORT = 464;
071    
072        /** The default encryption types. */
073        public static final String[] ENCRYPTION_TYPES_DEFAULT = new String[]
074            { "des-cbc-md5" };
075    
076        /** The default changepw buffer size. */
077        private static final long DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * 60000;
078    
079        /** The default empty addresses. */
080        private static final boolean DEFAULT_EMPTY_ADDRESSES_ALLOWED = true;
081    
082        /** The default change password password policy for password length. */
083        public static final int DEFAULT_PASSWORD_LENGTH = 6;
084    
085        /** The default change password password policy for category count. */
086        public static final int DEFAULT_CATEGORY_COUNT = 3;
087    
088        /** The default change password password policy for token size. */
089        public static final int DEFAULT_TOKEN_SIZE = 3;
090    
091        /** The default service PID. */
092        private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.changepw";
093    
094        /** The default service name. */
095        private static final String SERVICE_NAME_DEFAULT = "ApacheDS Change Password Service";
096    
097        /** The encryption types. */
098        private EncryptionType[] encryptionTypes;
099    
100        /** The primary realm. */
101        private String primaryRealm = REALM_DEFAULT;
102    
103        /** The service principal name. */
104        private String servicePrincipal = SERVICE_PRINCIPAL_DEFAULT;
105    
106        /** The allowable clock skew. */
107        private long allowableClockSkew = DEFAULT_ALLOWABLE_CLOCKSKEW;
108    
109        /** Whether empty addresses are allowed. */
110        private boolean isEmptyAddressesAllowed = DEFAULT_EMPTY_ADDRESSES_ALLOWED;
111    
112        /** The policy for password length. */
113        private int policyPasswordLength;
114    
115        /** The policy for category count. */
116        private int policyCategoryCount;
117    
118        /** The policy for token size. */
119        private int policyTokenSize;
120    
121    
122        /**
123         * Creates a new instance of ChangePasswordConfiguration.
124         */
125        public ChangePasswordServer()
126        {
127            super.setServiceName( SERVICE_NAME_DEFAULT );
128            super.setServiceId( SERVICE_PID_DEFAULT );
129            super.setSearchBaseDn( ServerDNConstants.USER_EXAMPLE_COM_DN );
130            setTransports( new TcpTransport( DEFAULT_IP_PORT ), new UdpTransport( DEFAULT_IP_PORT ) );
131    
132            prepareEncryptionTypes();
133        }
134    
135    
136        /**
137         * Returns the primary realm.
138         *
139         * @return The primary realm.
140         */
141        public String getPrimaryRealm()
142        {
143            return primaryRealm;
144        }
145    
146    
147        /**
148         * @param primaryRealm The primaryRealm to set.
149         */
150        public void setPrimaryRealm( String primaryRealm )
151        {
152            this.primaryRealm = primaryRealm;
153        }
154    
155    
156        /**
157         * Returns the encryption types.
158         *
159         * @return The encryption types.
160         */
161        public EncryptionType[] getEncryptionTypes()
162        {
163            return encryptionTypes;
164        }
165    
166    
167        /**
168         * @param encryptionTypes The encryptionTypes to set.
169         */
170        public void setEncryptionTypes( EncryptionType[] encryptionTypes )
171        {
172            this.encryptionTypes = encryptionTypes;
173        }
174    
175    
176        /**
177         * Returns the allowable clock skew.
178         *
179         * @return The allowable clock skew.
180         */
181        public long getAllowableClockSkew()
182        {
183            return allowableClockSkew;
184        }
185    
186    
187        /**
188         * @param allowableClockSkew The allowableClockSkew to set.
189         */
190        public void setAllowableClockSkew( long allowableClockSkew )
191        {
192            this.allowableClockSkew = allowableClockSkew;
193        }
194    
195    
196        /**
197         * Returns the Change Password service principal.
198         *
199         * @return The Change Password service principal.
200         */
201        public KerberosPrincipal getServicePrincipal()
202        {
203            return new KerberosPrincipal( servicePrincipal );
204        }
205    
206    
207        /**
208         * @param servicePrincipal The Change Password service principal to set.
209         */
210        public void setServicePrincipal( String servicePrincipal )
211        {
212            this.servicePrincipal = servicePrincipal;
213        }
214    
215    
216        /**
217         * Returns whether empty addresses are allowed.
218         *
219         * @return Whether empty addresses are allowed.
220         */
221        public boolean isEmptyAddressesAllowed()
222        {
223            return isEmptyAddressesAllowed;
224        }
225    
226    
227        /**
228         * @param isEmptyAddressesAllowed The isEmptyAddressesAllowed to set.
229         */
230        public void setEmptyAddressesAllowed( boolean isEmptyAddressesAllowed )
231        {
232            this.isEmptyAddressesAllowed = isEmptyAddressesAllowed;
233        }
234    
235    
236        /**
237         * Returns the password length.
238         *
239         * @return The password length.
240         */
241        public int getPasswordLengthPolicy()
242        {
243            return policyPasswordLength;
244        }
245    
246    
247        /**
248         * Returns the category count.
249         *
250         * @return The category count.
251         */
252        public int getCategoryCountPolicy()
253        {
254            return policyCategoryCount;
255        }
256    
257    
258        /**
259         * Returns the token size.
260         *
261         * @return The token size.
262         */
263        public int getTokenSizePolicy()
264        {
265            return policyTokenSize;
266        }
267    
268    
269        /**
270         * @throws IOException if we cannot bind to the specified ports
271         */
272        public void start() throws IOException, LdapInvalidDnException
273        {
274            PrincipalStore store = new DirectoryPrincipalStore( getDirectoryService(), new DN(this.getSearchBaseDn())  );
275            
276            if ( ( transports == null ) || ( transports.size() == 0 ) )
277            {
278                // Default to UDP with port 464
279                // We have to create a DatagramAcceptor
280                UdpTransport transport = new UdpTransport( DEFAULT_IP_PORT );
281                setTransports( transport );
282                
283                DatagramAcceptor acceptor = (DatagramAcceptor)transport.getAcceptor();
284    
285                // Set the handler
286                acceptor.setHandler( new ChangePasswordProtocolHandler( this, store )  );
287        
288                // Allow the port to be reused even if the socket is in TIME_WAIT state
289                ((DatagramSessionConfig)acceptor.getSessionConfig()).setReuseAddress( true );
290        
291                // Start the listener
292                acceptor.bind();
293            }
294            else
295            {
296                for ( Transport transport:transports )
297                {
298                    IoAcceptor acceptor = transport.getAcceptor();
299    
300                    // Disable the disconnection of the clients on unbind
301                    acceptor.setCloseOnDeactivation( false );
302                    
303                    if ( transport instanceof UdpTransport )
304                    {
305                        // Allow the port to be reused even if the socket is in TIME_WAIT state
306                        ((DatagramSessionConfig)acceptor.getSessionConfig()).setReuseAddress( true );
307                    }
308                    else
309                    {
310                        // Allow the port to be reused even if the socket is in TIME_WAIT state
311                        ((SocketAcceptor)acceptor).setReuseAddress( true );
312                        
313                        // No Nagle's algorithm
314                        ((SocketAcceptor)acceptor).getSessionConfig().setTcpNoDelay( true );
315                    }
316                    
317                    // Set the handler
318                    acceptor.setHandler( new ChangePasswordProtocolHandler( this, store ) );
319                    
320                    // Bind
321                    acceptor.bind();
322                }
323            }
324            
325            LOG.info( "ChangePassword service started." );
326            System.out.println( "ChangePassword service started." );
327        }
328    
329    
330        public void stop()
331        {
332            for ( Transport transport :getTransports() )
333            {
334                IoAcceptor acceptor = transport.getAcceptor();
335                
336                if ( acceptor != null )
337                {
338                    acceptor.dispose();
339                }
340            }
341    
342            LOG.info( "ChangePassword service stopped." );
343            System.out.println( "ChangePassword service stopped." );
344        }
345    
346    
347        private void prepareEncryptionTypes()
348        {
349            String[] encryptionTypeStrings = ENCRYPTION_TYPES_DEFAULT;
350            List<EncryptionType> encTypes = new ArrayList<EncryptionType>();
351    
352            for ( String enc : encryptionTypeStrings )
353            {
354                for ( EncryptionType type : EncryptionType.getEncryptionTypes() )
355                {
356                    if ( type.toString().equalsIgnoreCase( enc ) )
357                    {
358                        encTypes.add( type );
359                    }
360                }
361            }
362    
363            encryptionTypes = encTypes.toArray( new EncryptionType[encTypes.size()] );
364        }
365    
366    
367        /**
368         * Sets the policy's minimum?? password length.
369         *
370         * @param policyPasswordLength the minimum password length requirement
371         */
372        public void setPolicyPasswordLength( int policyPasswordLength )
373        {
374            this.policyPasswordLength = policyPasswordLength;
375        }
376    
377    
378        /**
379         * Sets the policy category count - what's this?
380         *
381         * @param policyCategoryCount the policy category count
382         */
383        public void setPolicyCategoryCount( int policyCategoryCount )
384        {
385            this.policyCategoryCount = policyCategoryCount;
386        }
387    
388    
389        /**
390         * Sets the policy token size - what's this?
391         *
392         * @param policyTokenSize the policy token size
393         */
394        public void setPolicyTokenSize( int policyTokenSize )
395        {
396            this.policyTokenSize = policyTokenSize;
397        }
398        
399        
400        /**
401         * @see Object#toString()
402         */
403        public String toString()
404        {
405            StringBuilder sb = new StringBuilder();
406            
407            sb.append( "ChangePasswordServer[" ).append( getServiceName() ).append( "], listening on :" ).append( '\n' );
408            
409            if ( getTransports() != null )
410            {
411                for ( Transport transport:getTransports() )
412                {
413                    sb.append( "    " ).append( transport ).append( '\n' );
414                }
415            }
416            
417            return sb.toString();
418        }
419    }