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 16:33:59 +0200 (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 }