1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.kerberos.kdc;
21  
22  
23  import org.apache.directory.server.core.DirectoryService;
24  import org.apache.directory.server.core.entry.ServerEntry;
25  import org.apache.directory.server.core.interceptor.Interceptor;
26  import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
27  import org.apache.directory.server.core.partition.Partition;
28  import org.apache.directory.server.xdbm.Index;
29  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
30  import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
31  import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
32  import org.apache.directory.server.unit.AbstractServerTest;
33  import org.apache.directory.shared.ldap.constants.SchemaConstants;
34  import org.apache.directory.shared.ldap.name.LdapDN;
35  
36  import javax.naming.Context;
37  import javax.naming.NamingException;
38  import javax.naming.directory.Attribute;
39  import javax.naming.directory.Attributes;
40  import javax.naming.directory.BasicAttribute;
41  import javax.naming.directory.BasicAttributes;
42  import javax.naming.directory.DirContext;
43  import javax.naming.directory.InitialDirContext;
44  import javax.naming.directory.ModificationItem;
45  
46  import java.util.HashSet;
47  import java.util.Hashtable;
48  import java.util.List;
49  import java.util.Set;
50  
51  
52  /**
53   * An {@link AbstractServerTest} testing SASL GSSAPI authentication
54   * and security layer negotiation.  These tests require both the LDAP
55   * and the Kerberos protocol.  As with any "three-headed" Kerberos
56   * scenario, there are 3 principals:  1 for the test user, 1 for the
57   * Kerberos ticket-granting service (TGS), and 1 for the LDAP service.
58   *
59   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
60   * @version $Rev$, $Date$
61   */
62  public class SaslGssapiBindITest extends AbstractServerTest
63  {
64      private DirContext ctx;
65  
66  
67      /**
68       * Creates a new instance of SaslGssapiBindTest and sets JAAS system properties
69       * for the KDC and realm, so we don't have to rely on external configuration.
70       */
71      public SaslGssapiBindITest()
72      {
73          System.setProperty( "java.security.krb5.realm", "EXAMPLE.COM" );
74          System.setProperty( "java.security.krb5.kdc", "localhost" );
75      }
76  
77  
78      /**
79       * Set up a partition for EXAMPLE.COM and add user and service principals to
80       * test authentication with.
81       */
82      public void setUp() throws Exception
83      {
84          super.setUp();
85  
86          ldapService.setSaslHost( "localhost" );
87          ldapService.setSaslPrincipal( "ldap/localhost@EXAMPLE.COM" );
88  
89          KdcServer kdcConfig = new KdcServer();
90          kdcConfig.setSocketAcceptor( socketAcceptor );
91          kdcConfig.setDirectoryService( directoryService );
92          kdcConfig.setEnabled( true );
93          kdcConfig.setSearchBaseDn( "ou=users,dc=example,dc=com" );
94  
95          Attributes attrs;
96  
97          setContexts( "uid=admin,ou=system", "secret" );
98  
99          // -------------------------------------------------------------------
100         // Enable the krb5kdc schema
101         // -------------------------------------------------------------------
102 
103         // check if krb5kdc is disabled
104         Attributes krb5kdcAttrs = schemaRoot.getAttributes( "cn=Krb5kdc" );
105         boolean isKrb5KdcDisabled = false;
106         if ( krb5kdcAttrs.get( "m-disabled" ) != null )
107         {
108             isKrb5KdcDisabled = ( ( String ) krb5kdcAttrs.get( "m-disabled" ).get() ).equalsIgnoreCase( "TRUE" );
109         }
110 
111         // if krb5kdc is disabled then enable it
112         if ( isKrb5KdcDisabled )
113         {
114             Attribute disabled = new BasicAttribute( "m-disabled" );
115             ModificationItem[] mods = new ModificationItem[]
116                     {new ModificationItem( DirContext.REMOVE_ATTRIBUTE, disabled )};
117             schemaRoot.modifyAttributes( "cn=Krb5kdc", mods );
118         }
119         
120         LdapDN contextDn = new LdapDN( "dc=example,dc=com" );
121         ServerEntry entry = ldapService.getDirectoryService().newEntry( contextDn );
122         entry.add( "objectClass", "top", "domain", "extensibleObject" );
123         entry.add( "dc", "example" );
124         ldapService.getDirectoryService().getAdminSession().add( entry );
125 
126         // Get a context, create the ou=users subcontext, then create the 3 principals.
127         Hashtable<String, Object> env = new Hashtable<String, Object>();
128         env.put( DirectoryService.JNDI_KEY, directoryService );
129         env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
130         env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
131         env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
132         env.put( Context.SECURITY_CREDENTIALS, "secret" );
133         env.put( Context.SECURITY_AUTHENTICATION, "simple" );
134 
135         ctx = new InitialDirContext( env );
136 
137         attrs = getOrgUnitAttributes( "users" );
138         DirContext users = ctx.createSubcontext( "ou=users", attrs );
139 
140         attrs = getPrincipalAttributes( "Nelson", "Horatio Nelson", "hnelson", "secret", "hnelson@EXAMPLE.COM" );
141         users.createSubcontext( "uid=hnelson", attrs );
142 
143         attrs = getPrincipalAttributes( "Service", "KDC Service", "krbtgt", "secret", "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
144         users.createSubcontext( "uid=krbtgt", attrs );
145 
146         attrs = getPrincipalAttributes( "Service", "LDAP Service", "ldap", "randall", "ldap/localhost@EXAMPLE.COM" );
147         users.createSubcontext( "uid=ldap", attrs );
148     }
149 
150     
151     protected void configureDirectoryService() throws NamingException
152     {
153         directoryService.setAllowAnonymousAccess( false );
154         Set<Partition> partitions = new HashSet<Partition>();
155 
156         // Add partition 'example'
157         JdbmPartition partition = new JdbmPartition();
158         partition.setId( "example" );
159         partition.setSuffix( "dc=example,dc=com" );
160 
161         Set<Index<?,ServerEntry>> indexedAttrs = new HashSet<Index<?,ServerEntry>>();
162         indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "ou" ) );
163         indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "dc" ) );
164         indexedAttrs.add( new JdbmIndex<String,ServerEntry>( "objectClass" ) );
165         partition.setIndexedAttributes( indexedAttrs );
166 
167         partitions.add( partition );
168         directoryService.setPartitions( partitions );
169 
170         List<Interceptor> list = directoryService.getInterceptors();
171         list.add( new KeyDerivationInterceptor() );
172         directoryService.setInterceptors( list );
173     }
174 
175 
176     /**
177      * Convenience method for creating principals.
178      *
179      * @param cn           the commonName of the person
180      * @param principal    the kerberos principal name for the person
181      * @param sn           the surName of the person
182      * @param uid          the unique identifier for the person
183      * @param userPassword the credentials of the person
184      * @return the attributes of the person principal
185      */
186     protected Attributes getPrincipalAttributes( String sn, String cn, String uid, String userPassword, String principal )
187     {
188         Attributes attrs = new BasicAttributes( true );
189         Attribute ocls = new BasicAttribute( "objectClass" );
190         ocls.add( "top" );
191         ocls.add( "person" ); // sn $ cn
192         ocls.add( "inetOrgPerson" ); // uid
193         ocls.add( "krb5principal" );
194         ocls.add( "krb5kdcentry" );
195         attrs.put( ocls );
196         attrs.put( "cn", cn );
197         attrs.put( "sn", sn );
198         attrs.put( "uid", uid );
199         attrs.put( SchemaConstants.USER_PASSWORD_AT, userPassword );
200         attrs.put( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal );
201         attrs.put( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, "0" );
202 
203         return attrs;
204     }
205 
206 
207     /**
208      * Convenience method for creating an organizational unit.
209      *
210      * @param ou the ou of the organizationalUnit
211      * @return the attributes of the organizationalUnit
212      */
213     protected Attributes getOrgUnitAttributes( String ou )
214     {
215         Attributes attrs = new BasicAttributes();
216         Attribute ocls = new BasicAttribute( "objectClass" );
217         ocls.add( "top" );
218         ocls.add( "organizationalUnit" );
219         attrs.put( ocls );
220         attrs.put( "ou", ou );
221 
222         return attrs;
223     }
224 
225 
226     /**
227      * Tests to make sure GSSAPI binds below the RootDSE work.
228      */
229     public void testSaslGssapiBind()
230     {
231         assertTrue( true );
232     }
233 
234 
235     /**
236      * Tear down.
237      */
238     public void tearDown() throws Exception
239     {
240         ctx.close();
241         ctx = null;
242         super.tearDown();
243     }
244 }