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.kerberos.kdc;
021    
022    
023    import java.io.IOException;
024    import java.util.HashSet;
025    import java.util.Set;
026    
027    import javax.security.auth.kerberos.KerberosPrincipal;
028    
029    import org.apache.directory.server.constants.ServerDNConstants;
030    import org.apache.directory.server.kerberos.protocol.KerberosProtocolHandler;
031    import org.apache.directory.server.kerberos.protocol.KerberosTcpProtocolCodecFactory;
032    import org.apache.directory.server.kerberos.protocol.KerberosUdpProtocolCodecFactory;
033    import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
034    import org.apache.directory.server.kerberos.shared.store.DirectoryPrincipalStore;
035    import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
036    import org.apache.directory.server.protocol.shared.DirectoryBackedService;
037    import org.apache.directory.server.protocol.shared.transport.TcpTransport;
038    import org.apache.directory.server.protocol.shared.transport.Transport;
039    import org.apache.directory.server.protocol.shared.transport.UdpTransport;
040    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
041    import org.apache.directory.shared.ldap.name.DN;
042    import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
043    import org.apache.mina.core.filterchain.IoFilterChainBuilder;
044    import org.apache.mina.core.service.IoAcceptor;
045    import org.apache.mina.filter.codec.ProtocolCodecFilter;
046    import org.apache.mina.transport.socket.DatagramAcceptor;
047    import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
048    import org.slf4j.Logger;
049    import org.slf4j.LoggerFactory;
050    
051    
052    /**
053     * Contains the configuration parameters for the Kerberos protocol provider.
054     *
055     * @org.apache.xbean.XBean
056     *
057     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
058     * @version $Rev: 923757 $, $Date: 2010-03-16 15:33:59 +0100 (Tue, 16 Mar 2010) $
059     */
060    public class KdcServer extends DirectoryBackedService
061    {
062        private static final long serialVersionUID = 522567370475574165L;
063    
064        /** logger for this class */
065        private static final Logger LOG = LoggerFactory.getLogger( KdcServer.class.getName() );
066        
067        
068        /** The default kdc port */
069        private static final int DEFAULT_IP_PORT = 88;
070    
071        /** The default kdc service pid */
072        private static final String DEFAULT_PID = "org.apache.directory.server.kerberos";
073    
074        /** The default kdc service name */
075        private static final String DEFAULT_NAME = "ApacheDS Kerberos Service";
076    
077        /** The default kdc service principal */
078        private static final String DEFAULT_PRINCIPAL = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
079    
080        /** The default kdc realm */
081        private static final String DEFAULT_REALM = "EXAMPLE.COM";
082    
083        /** The default allowable clockskew */
084        private static final long DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * 60000;
085    
086        /** The default encryption types */
087        private static final String[] DEFAULT_ENCRYPTION_TYPES = new String[]
088            { "des-cbc-md5" };
089    
090        /** The default for allowing empty addresses */
091        private static final boolean DEFAULT_EMPTY_ADDRESSES_ALLOWED = true;
092    
093        /** The default for requiring encrypted timestamps */
094        private static final boolean DEFAULT_PA_ENC_TIMESTAMP_REQUIRED = true;
095    
096        /** The default for the maximum ticket lifetime */
097        private static final int DEFAULT_TGS_MAXIMUM_TICKET_LIFETIME = 60000 * 1440;
098    
099        /** The default for the maximum renewable lifetime */
100        private static final int DEFAULT_TGS_MAXIMUM_RENEWABLE_LIFETIME = 60000 * 10080;
101    
102        /** The default for allowing forwardable tickets */
103        private static final boolean DEFAULT_TGS_FORWARDABLE_ALLOWED = true;
104    
105        /** The default for allowing proxiable tickets */
106        private static final boolean DEFAULT_TGS_PROXIABLE_ALLOWED = true;
107    
108        /** The default for allowing postdated tickets */
109        private static final boolean DEFAULT_TGS_POSTDATED_ALLOWED = true;
110    
111        /** The default for allowing renewable tickets */
112        private static final boolean DEFAULT_TGS_RENEWABLE_ALLOWED = true;
113    
114        /** The default for verifying the body checksum */
115        private static final boolean DEFAULT_VERIFY_BODY_CHECKSUM = true;
116    
117        /** The encryption types. */
118        private Set<EncryptionType> encryptionTypes;
119    
120        /** The primary realm */
121        private String primaryRealm = DEFAULT_REALM;
122    
123        /** The service principal name. */
124        private String servicePrincipal = DEFAULT_PRINCIPAL;
125    
126        /** The allowable clock skew. */
127        private long allowableClockSkew = DEFAULT_ALLOWABLE_CLOCKSKEW;
128    
129        /** Whether pre-authentication by encrypted timestamp is required. */
130        private boolean isPaEncTimestampRequired = DEFAULT_PA_ENC_TIMESTAMP_REQUIRED;
131    
132        /** The maximum ticket lifetime. */
133        private long maximumTicketLifetime = DEFAULT_TGS_MAXIMUM_TICKET_LIFETIME;
134    
135        /** The maximum renewable lifetime. */
136        private long maximumRenewableLifetime = DEFAULT_TGS_MAXIMUM_RENEWABLE_LIFETIME;
137    
138        /** Whether empty addresses are allowed. */
139        private boolean isEmptyAddressesAllowed = DEFAULT_EMPTY_ADDRESSES_ALLOWED;
140    
141        /** Whether forwardable addresses are allowed. */
142        private boolean isForwardableAllowed = DEFAULT_TGS_FORWARDABLE_ALLOWED;
143    
144        /** Whether proxiable addresses are allowed. */
145        private boolean isProxiableAllowed = DEFAULT_TGS_PROXIABLE_ALLOWED;
146    
147        /** Whether postdated tickets are allowed. */
148        private boolean isPostdatedAllowed = DEFAULT_TGS_POSTDATED_ALLOWED;
149    
150        /** Whether renewable tickets are allowed. */
151        private boolean isRenewableAllowed = DEFAULT_TGS_RENEWABLE_ALLOWED;
152    
153        /** Whether to verify the body checksum. */
154        private boolean isBodyChecksumVerified = DEFAULT_VERIFY_BODY_CHECKSUM;
155    
156    
157        /**
158         * Creates a new instance of KdcConfiguration.
159         */
160        public KdcServer()
161        {
162            super.setServiceName( DEFAULT_NAME );
163            super.setServiceId( DEFAULT_PID );
164            super.setSearchBaseDn( ServerDNConstants.USER_EXAMPLE_COM_DN );
165    
166            prepareEncryptionTypes();
167        }
168    
169    
170        /**
171         * Returns the allowable clock skew.
172         *
173         * @return The allowable clock skew.
174         */
175        public long getAllowableClockSkew()
176        {
177            return allowableClockSkew;
178        }
179    
180    
181        /**
182         * @return the isEmptyAddressesAllowed
183         */
184        public boolean isEmptyAddressesAllowed()
185        {
186            return isEmptyAddressesAllowed;
187        }
188    
189    
190        /**
191         * @return the isForwardableAllowed
192         */
193        public boolean isForwardableAllowed()
194        {
195            return isForwardableAllowed;
196        }
197    
198    
199        /**
200         * @return the isPostdatedAllowed
201         */
202        public boolean isPostdatedAllowed()
203        {
204            return isPostdatedAllowed;
205        }
206    
207    
208        /**
209         * @return the isProxiableAllowed
210         */
211        public boolean isProxiableAllowed()
212        {
213            return isProxiableAllowed;
214        }
215    
216    
217        /**
218         * @return the isRenewableAllowed
219         */
220        public boolean isRenewableAllowed()
221        {
222            return isRenewableAllowed;
223        }
224    
225    
226        /**
227         * @return the maximumRenewableLifetime
228         */
229        public long getMaximumRenewableLifetime()
230        {
231            return maximumRenewableLifetime;
232        }
233    
234    
235        /**
236         * @return the maximumTicketLifetime
237         */
238        public long getMaximumTicketLifetime()
239        {
240            return maximumTicketLifetime;
241        }
242    
243    
244        /**
245         * @param allowableClockSkew the allowableClockSkew to set
246         */
247        public void setAllowableClockSkew( long allowableClockSkew )
248        {
249            this.allowableClockSkew = allowableClockSkew;
250        }
251    
252    
253        /**
254         * Initialize the encryptionTypes set
255         * 
256         * @param encryptionTypes the encryptionTypes to set
257         */
258        public void setEncryptionTypes( EncryptionType[] encryptionTypes )
259        {
260            if ( encryptionTypes != null )
261            {
262                this.encryptionTypes.clear();
263                
264                for ( EncryptionType encryptionType:encryptionTypes )
265                {
266                    this.encryptionTypes.add( encryptionType );
267                }
268            }
269        }
270    
271    
272        /**
273         * Initialize the encryptionTypes set
274         * 
275         * @param encryptionTypes the encryptionTypes to set
276         */
277        public void setEncryptionTypes( Set<EncryptionType> encryptionTypes )
278        {
279            this.encryptionTypes = encryptionTypes;
280        }
281    
282    
283        /**
284         * @param isEmptyAddressesAllowed the isEmptyAddressesAllowed to set
285         */
286        public void setEmptyAddressesAllowed( boolean isEmptyAddressesAllowed )
287        {
288            this.isEmptyAddressesAllowed = isEmptyAddressesAllowed;
289        }
290    
291    
292        /**
293         * @param isForwardableAllowed the isForwardableAllowed to set
294         */
295        public void setForwardableAllowed( boolean isForwardableAllowed )
296        {
297            this.isForwardableAllowed = isForwardableAllowed;
298        }
299    
300    
301        /**
302         * @param isPaEncTimestampRequired the isPaEncTimestampRequired to set
303         */
304        public void setPaEncTimestampRequired( boolean isPaEncTimestampRequired )
305        {
306            this.isPaEncTimestampRequired = isPaEncTimestampRequired;
307        }
308    
309    
310        /**
311         * @param isPostdatedAllowed the isPostdatedAllowed to set
312         */
313        public void setPostdatedAllowed( boolean isPostdatedAllowed )
314        {
315            this.isPostdatedAllowed = isPostdatedAllowed;
316        }
317    
318    
319        /**
320         * @param isProxiableAllowed the isProxiableAllowed to set
321         */
322        public void setProxiableAllowed( boolean isProxiableAllowed )
323        {
324            this.isProxiableAllowed = isProxiableAllowed;
325        }
326    
327    
328        /**
329         * @param isRenewableAllowed the isRenewableAllowed to set
330         */
331        public void setRenewableAllowed( boolean isRenewableAllowed )
332        {
333            this.isRenewableAllowed = isRenewableAllowed;
334        }
335    
336    
337        /**
338         * @param kdcPrincipal the kdcPrincipal to set
339         */
340        public void setKdcPrincipal( String kdcPrincipal )
341        {
342            this.servicePrincipal = kdcPrincipal;
343        }
344    
345    
346        /**
347         * @param maximumRenewableLifetime the maximumRenewableLifetime to set
348         */
349        public void setMaximumRenewableLifetime( long maximumRenewableLifetime )
350        {
351            this.maximumRenewableLifetime = maximumRenewableLifetime;
352        }
353    
354    
355        /**
356         * @param maximumTicketLifetime the maximumTicketLifetime to set
357         */
358        public void setMaximumTicketLifetime( long maximumTicketLifetime )
359        {
360            this.maximumTicketLifetime = maximumTicketLifetime;
361        }
362    
363    
364        /**
365         * @param primaryRealm the primaryRealm to set
366         */
367        public void setPrimaryRealm( String primaryRealm )
368        {
369            this.primaryRealm = primaryRealm;
370        }
371    
372    
373        /**
374         * Returns the primary realm.
375         *
376         * @return The primary realm.
377         */
378        public String getPrimaryRealm()
379        {
380            return primaryRealm;
381        }
382    
383    
384        /**
385         * Returns the service principal for this KDC service.
386         *
387         * @return The service principal for this KDC service.
388         */
389        public KerberosPrincipal getServicePrincipal()
390        {
391            return new KerberosPrincipal( servicePrincipal );
392        }
393    
394    
395        /**
396         * Returns the encryption types.
397         *
398         * @return The encryption types.
399         */
400        public Set<EncryptionType> getEncryptionTypes()
401        {
402            return encryptionTypes;
403        }
404    
405    
406        /**
407         * Returns whether pre-authentication by encrypted timestamp is required.
408         *
409         * @return Whether pre-authentication by encrypted timestamp is required.
410         */
411        public boolean isPaEncTimestampRequired()
412        {
413            return isPaEncTimestampRequired;
414        }
415    
416    
417        /**
418         * @return the isBodyChecksumVerified
419         */
420        public boolean isBodyChecksumVerified()
421        {
422            return isBodyChecksumVerified;
423        }
424    
425    
426        /**
427         * @param isBodyChecksumVerified the isBodyChecksumVerified to set
428         */
429        public void setBodyChecksumVerified( boolean isBodyChecksumVerified )
430        {
431            this.isBodyChecksumVerified = isBodyChecksumVerified;
432        }
433    
434    
435        /**
436         * @throws IOException if we cannot bind to the sockets
437         */
438        public void start() throws IOException, LdapInvalidDnException
439        {
440            PrincipalStore store;
441    
442            // TODO - for now ignoring this catalog crap
443            store = new DirectoryPrincipalStore( getDirectoryService(), new DN(this.getSearchBaseDn())  );
444            
445            if ( ( transports == null ) || ( transports.size() == 0 ) )
446            {
447                // Default to UDP with port 88
448                // We have to create a DatagramAcceptor
449                UdpTransport transport = new UdpTransport( DEFAULT_IP_PORT );
450                setTransports( transport );
451                
452                DatagramAcceptor acceptor = (DatagramAcceptor)transport.getAcceptor();
453    
454                // Inject the chain
455                IoFilterChainBuilder udpChainBuilder = new DefaultIoFilterChainBuilder();
456    
457                ((DefaultIoFilterChainBuilder)udpChainBuilder).addFirst( "codec", 
458                        new ProtocolCodecFilter( 
459                                KerberosUdpProtocolCodecFactory.getInstance() ) );
460    
461                acceptor.setFilterChainBuilder( udpChainBuilder );
462    
463                // Inject the protocol handler
464                acceptor.setHandler( new KerberosProtocolHandler( this, store ) );
465                
466                // Bind to the configured address
467                acceptor.bind();
468            }
469            else
470            {
471                // Kerberos can use UDP or TCP
472                for ( Transport transport:transports )
473                {
474                    IoAcceptor acceptor = transport.getAcceptor();
475                    
476                    // Now, configure the acceptor
477                    // Inject the chain
478                    IoFilterChainBuilder chainBuilder = new DefaultIoFilterChainBuilder();
479        
480                    if ( transport instanceof TcpTransport )
481                    {
482                        // Now, configure the acceptor
483                        // Disable the disconnection of the clients on unbind
484                        acceptor.setCloseOnDeactivation( false );
485                        
486                        // No Nagle's algorithm
487                        ((NioSocketAcceptor)acceptor).getSessionConfig().setTcpNoDelay( true );
488                        
489                        // Allow the port to be reused even if the socket is in TIME_WAIT state
490                        ((NioSocketAcceptor)acceptor).setReuseAddress( true );
491    
492                        // Inject the codec
493                        ((DefaultIoFilterChainBuilder)chainBuilder).addFirst( "codec", 
494                            new ProtocolCodecFilter( 
495                                    KerberosTcpProtocolCodecFactory.getInstance() ) );
496                    }
497                    else
498                    {
499                        // Inject the codec
500                        ((DefaultIoFilterChainBuilder)chainBuilder).addFirst( "codec", 
501                            new ProtocolCodecFilter( 
502                                    KerberosUdpProtocolCodecFactory.getInstance() ) );
503                    }
504    
505                    acceptor.setFilterChainBuilder( chainBuilder );
506                    
507                    // Inject the protocol handler
508                    acceptor.setHandler( new KerberosProtocolHandler( this, store ) );
509                    
510                    // Bind to the configured address
511                    acceptor.bind();
512                }
513            }
514            
515            LOG.info( "Kerberos service started." );
516            System.out.println( "Kerberos service started." );
517        }
518    
519        
520        public void stop()
521        {
522            for ( Transport transport :getTransports() )
523            {
524                IoAcceptor acceptor = transport.getAcceptor();
525                
526                if ( acceptor != null )
527                {
528                    acceptor.dispose();
529                }
530            }
531            
532            LOG.info( "Kerberos service stopped." );
533            System.out.println( "Kerberos service stopped." );
534        }
535    
536    
537        /**
538         * Construct an HashSet containing the default encryption types
539         */
540        private void prepareEncryptionTypes()
541        {
542            String[] encryptionTypeStrings = DEFAULT_ENCRYPTION_TYPES;
543    
544            encryptionTypes = new HashSet<EncryptionType>();
545    
546            for ( String enc : encryptionTypeStrings )
547            {
548                for ( EncryptionType type : EncryptionType.getEncryptionTypes() )
549                {
550                    if ( type.getName().equalsIgnoreCase( enc ) )
551                    {
552                        encryptionTypes.add( type );
553                    }
554                }
555            }
556        }
557        
558        
559        /**
560         * @see Object#toString()
561         */
562        public String toString()
563        {
564            StringBuilder sb = new StringBuilder();
565            
566            sb.append( "KDCServer[" ).append( getServiceName() ).append( "], listening on :" ).append( '\n' );
567            
568            if ( getTransports() != null )
569            {
570                for ( Transport transport:getTransports() )
571                {
572                    sb.append( "    " ).append( transport ).append( '\n' );
573                }
574            }
575            
576            return sb.toString();
577        }
578    }