001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2007-2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.admin.ads;
029    
030    import java.util.Map;
031    import java.util.SortedSet;
032    import java.util.TreeSet;
033    
034    import javax.naming.NameNotFoundException;
035    import javax.naming.NamingEnumeration;
036    import javax.naming.NamingException;
037    import javax.naming.directory.Attribute;
038    import javax.naming.directory.BasicAttribute;
039    import javax.naming.directory.BasicAttributes;
040    import javax.naming.directory.SearchResult;
041    import javax.naming.ldap.InitialLdapContext;
042    import javax.naming.ldap.LdapName;
043    import javax.naming.ldap.Rdn;
044    
045    import org.opends.admin.ads.ADSContext.ServerProperty;
046    import org.opends.server.admin.ManagedObjectNotFoundException;
047    import org.opends.server.admin.client.ManagementContext;
048    import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
049    import org.opends.server.admin.client.ldap.LDAPManagementContext;
050    import org.opends.server.admin.std.client.*;
051    import org.opends.server.admin.std.meta.BackendCfgDefn;
052    import org.opends.server.admin.std.meta.LDIFBackendCfgDefn;
053    import org.opends.server.config.ConfigConstants;
054    import org.opends.server.crypto.CryptoManagerImpl;
055    import org.opends.server.types.CryptoManagerException;
056    import org.opends.server.types.DN;
057    
058    /**
059     * This is the only class in the org.opends.admin.ads package that uses the
060     * classes from OpenDS.jar (in particular the administration client framework
061     * API).  Before calling this class OpenDS.jar must be
062     * loaded.  The goal is basically to centralize in one single place the
063     * dependencies of this package on OpenDS.jar.  This is done in order the
064     * QuickSetup code to be able to use some of the functionalities provided
065     * by the ADSContext classes before OpenDS.jar is downloaded.
066     */
067    public class ADSContextHelper
068    {
069      /**
070       * Default constructor.
071       */
072      public ADSContextHelper()
073      {
074      }
075      /**
076       * Removes the administration suffix.
077       * @param ctx the DirContext to be used.
078       * @param backendName the name of the backend where the administration
079       * suffix is stored.
080       * @throws ADSContextException if the administration suffix could not be
081       * removed.
082       */
083      public void removeAdministrationSuffix(InitialLdapContext ctx,
084          String backendName) throws ADSContextException
085      {
086        try
087        {
088          ManagementContext mCtx = LDAPManagementContext.createFromContext(
089              JNDIDirContextAdaptor.adapt(ctx));
090          RootCfgClient root = mCtx.getRootConfiguration();
091          BackendCfgClient backend = null;
092          try
093          {
094            backend = root.getBackend(backendName);
095          }
096          catch (ManagedObjectNotFoundException monfe)
097          {
098            // It does not exist.
099          }
100          if (backend != null)
101          {
102            SortedSet<DN> suffixes = backend.getBaseDN();
103            if (suffixes != null)
104            {
105              if (suffixes.remove(
106                  DN.decode(ADSContext.getAdministrationSuffixDN())))
107              {
108                if (suffixes.size() > 0)
109                {
110                  backend.setBaseDN(suffixes);
111                  backend.commit();
112                }
113                else
114                {
115                  root.removeBackend(backendName);
116                }
117              }
118            }
119          }
120        }
121        catch (Throwable t)
122        {
123          throw new ADSContextException(
124              ADSContextException.ErrorType.ERROR_UNEXPECTED, t);
125        }
126      }
127    
128      /**
129       * Creates the Administration Suffix.
130       * @param ctx the DirContext to be used.
131       * @param backendName the name of the backend where the administration
132       * suffix is stored.
133       * @throws ADSContextException if the administration suffix could not be
134       * created.
135       */
136      public void createAdministrationSuffix(InitialLdapContext ctx,
137          String backendName)
138      throws ADSContextException
139      {
140        try
141        {
142          ManagementContext mCtx = LDAPManagementContext.createFromContext(
143              JNDIDirContextAdaptor.adapt(ctx));
144          RootCfgClient root = mCtx.getRootConfiguration();
145          LDIFBackendCfgClient backend = null;
146          try
147          {
148            backend = (LDIFBackendCfgClient)root.getBackend(backendName);
149          }
150          catch (ManagedObjectNotFoundException e)
151          {
152          }
153          catch (ClassCastException cce)
154          {
155            throw new ADSContextException(
156                ADSContextException.ErrorType.UNEXPECTED_ADS_BACKEND_TYPE, cce);
157          }
158    
159          if (backend == null)
160          {
161            LDIFBackendCfgDefn provider = LDIFBackendCfgDefn.getInstance();
162            backend = root.createBackend(provider, backendName, null);
163            backend.setEnabled(true);
164            backend.setLDIFFile(ADSContext.getAdminLDIFFile());
165            backend.setBackendId(backendName);
166            backend.setWritabilityMode(BackendCfgDefn.WritabilityMode.ENABLED);
167            backend.setIsPrivateBackend(true);
168          }
169          SortedSet<DN> suffixes = backend.getBaseDN();
170          if (suffixes == null)
171          {
172            suffixes = new TreeSet<DN>();
173          }
174          DN newDN = DN.decode(ADSContext.getAdministrationSuffixDN());
175          if (!suffixes.contains(newDN))
176          {
177            suffixes.add(newDN);
178            backend.setBaseDN(suffixes);
179            backend.commit();
180          }
181        }
182        catch (Throwable t)
183        {
184          throw new ADSContextException(
185              ADSContextException.ErrorType.ERROR_UNEXPECTED, t);
186        }
187      }
188    
189      /**
190      Register instance key-pair public-key certificate provided in
191      serverProperties: generate a key-id attribute if one is not provided (as
192      expected); add an instance key public-key certificate entry for the key
193      certificate; and associate the certificate entry with the server entry via
194      the key ID attribute.
195      @param ctx the InitialLdapContext on the server we want to update.
196      @param serverProperties Properties of the server being registered to which
197      the instance key entry belongs.
198      @param serverEntryDn The server's ADS entry DN.
199      @throws ADSContextException In case some JNDI operation fails or there is a
200      problem getting the instance public key certificate ID.
201       */
202      public void registerInstanceKeyCertificate(
203          InitialLdapContext ctx, Map<ServerProperty, Object> serverProperties,
204          LdapName serverEntryDn)
205      throws ADSContextException {
206        assert serverProperties.containsKey(
207            ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE);
208        if (! serverProperties.containsKey(
209            ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE)) {
210          return;
211        }
212    
213        /* the key ID might be supplied in serverProperties (although, I am unaware
214       of any such case). */
215        String keyID = (String)serverProperties.get(ServerProperty.INSTANCE_KEY_ID);
216    
217        /* these attributes are used both to search for an existing certificate
218       entry and, if one does not exist, add a new certificate entry */
219        final BasicAttributes keyAttrs = new BasicAttributes();
220        final Attribute oc = new BasicAttribute("objectclass");
221        oc.add("top"); oc.add("ds-cfg-instance-key");
222        keyAttrs.put(oc);
223        if (null != keyID) {
224          keyAttrs.put(new BasicAttribute(
225              ServerProperty.INSTANCE_KEY_ID.getAttributeName(), keyID));
226        }
227        keyAttrs.put(new BasicAttribute(
228            ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE.getAttributeName()
229            + ";binary",
230            serverProperties.get(
231                ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE)));
232    
233        /* search for public-key certificate entry in ADS DIT */
234        final String attrIDs[] = { "ds-cfg-key-id" };
235        try
236        {
237          final NamingEnumeration<SearchResult> results = ctx.search(
238              ADSContext.getInstanceKeysContainerDN(), keyAttrs, attrIDs);
239          if (results.hasMore()) {
240            final Attribute keyIdAttr =
241              results.next().getAttributes().get(attrIDs[0]);
242            if (null != keyIdAttr) {
243              /* attribute ds-cfg-key-id is the entry is a MUST in the schema */
244              keyID = (String)keyIdAttr.get();
245            }
246          }
247          /* TODO: It is possible (but unexpected) that the caller specifies a
248       ds-cfg-key-id value for which there is a certificate entry in ADS, but
249       the certificate value does not match that supplied by the caller. The
250       above search would not return the entry, but the below attempt to add
251       an new entry with the supplied ds-cfg-key-id will fail (throw a
252       NameAlreadyBoundException) */
253          else {
254            /* create key ID, if it was not supplied in serverProperties */
255            if (null == keyID) {
256              keyID = CryptoManagerImpl.getInstanceKeyID(
257                  (byte[])serverProperties.get(
258                      ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE));
259              keyAttrs.put(new BasicAttribute(
260                  ServerProperty.INSTANCE_KEY_ID.getAttributeName(), keyID));
261            }
262    
263            /* add public-key certificate entry */
264            final LdapName keyDn = new LdapName((new StringBuilder())
265                .append(ServerProperty.INSTANCE_KEY_ID.getAttributeName())
266                .append("=").append(Rdn.escapeValue(keyID)).append(",")
267                .append(ADSContext.getInstanceKeysContainerDN()).toString());
268            ctx.createSubcontext(keyDn, keyAttrs).close();
269          }
270    
271          /* associate server entry with certificate entry via key ID attribute */
272          ctx.modifyAttributes(serverEntryDn,
273              InitialLdapContext.REPLACE_ATTRIBUTE,
274              (new BasicAttributes(
275                  ServerProperty.INSTANCE_KEY_ID.getAttributeName(), keyID)));
276        }
277        catch (NamingException ne)
278        {
279          throw new ADSContextException(
280              ADSContextException.ErrorType.ERROR_UNEXPECTED, ne);
281        }
282        catch (CryptoManagerException cme)
283        {
284          throw new ADSContextException(
285              ADSContextException.ErrorType.ERROR_UNEXPECTED, cme);
286        }
287      }
288    
289    
290      /**
291      Unregister instance key-pair public-key certificate provided in
292      serverProperties.
293      @param ctx the connection to the server.
294      @param serverProperties Properties of the server being unregistered to which
295      the instance key entry belongs.
296      @param serverEntryDn The server's ADS entry DN.
297      @throws ADSContextException In case some JNDI operation fails.
298      */
299      public void unregisterInstanceKeyCertificate(
300          InitialLdapContext ctx, Map<ServerProperty, Object> serverProperties,
301          LdapName serverEntryDn)
302      throws ADSContextException {
303        assert serverProperties.containsKey(
304            ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE);
305        if (! serverProperties.containsKey(
306            ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE)) {
307          return;
308        }
309    
310        /* these attributes are used both to search for an existing certificate
311         entry and, if one does not exist, add a new certificate entry */
312        final BasicAttributes keyAttrs = new BasicAttributes();
313        final Attribute oc = new BasicAttribute("objectclass");
314        oc.add("top"); oc.add("ds-cfg-instance-key");
315        keyAttrs.put(oc);
316        keyAttrs.put(new BasicAttribute(
317            ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE.getAttributeName()
318            + ";binary",
319            serverProperties.get(
320                ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE)));
321    
322        /* search for public-key certificate entry in ADS DIT */
323        final String attrIDs[] = { "ds-cfg-key-id" };
324        try
325        {
326          final NamingEnumeration<SearchResult> results = ctx.search(
327              ADSContext.getInstanceKeysContainerDN(), keyAttrs, attrIDs);
328          if (results.hasMore()) {
329            SearchResult res = results.next();
330            ctx.destroySubcontext(res.getNameInNamespace());
331          }
332        }
333        catch (NameNotFoundException nnfe)
334        {
335        }
336        catch (NamingException ne)
337        {
338          throw new ADSContextException(
339              ADSContextException.ErrorType.ERROR_UNEXPECTED, ne);
340        }
341      }
342    
343      /**
344       * Returns the crypto instance key objectclass name as defined in
345       * ConfigConstants.
346       * @return the crypto instance key objectclass name as defined in
347       * ConfigConstants.
348       */
349      public String getOcCryptoInstanceKey()
350      {
351        return ConfigConstants.OC_CRYPTO_INSTANCE_KEY;
352      }
353    
354      /**
355       * Returns the crypto key compromised time attribute name as defined in
356       * ConfigConstants.
357       * @return the crypto key compromised time attribute name as defined in
358       * ConfigConstants.
359       */
360      public String getAttrCryptoKeyCompromisedTime()
361      {
362        return ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME;
363      }
364    }