mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
23.26.2007 2861a1aad527dae9aea800386d58fc7416233ae5
opendj-sdk/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -33,18 +33,14 @@
import java.io.FileWriter;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.GSSAPISASLMechanismHandlerCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.IdentityMapper;
import org.opends.server.api.SASLMechanismHandler;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.config.DNConfigAttribute;
import org.opends.server.config.StringConfigAttribute;
import org.opends.server.core.BindOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.AuthenticationInfo;
@@ -55,7 +51,6 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
@@ -71,33 +66,21 @@
 * clients through Kerberos over GSSAPI.
 */
public class GSSAPISASLMechanismHandler
       extends SASLMechanismHandler
       implements ConfigurableComponent
       extends SASLMechanismHandler<GSSAPISASLMechanismHandlerCfg>
       implements ConfigurationChangeListener<
                       GSSAPISASLMechanismHandlerCfg>
{
  // The DN of the configuration entry for this SASL mechanism handler.
  private DN configEntryDN;
  // The DN of the identity mapper configuration entry.
  private DN identityMapperDN;
  // The current configuration for this SASL mechanism handler.
  private GSSAPISASLMechanismHandlerCfg currentConfig;
  // The identity mapper that will be used to map the Kerberos principal to a
  // directory user.
  private IdentityMapper identityMapper;
  // The address of the KDC to use for Kerberos authentication.
  private String kdcAddress;
  // The path to the keytab file to use to obtain the server key.
  private String keyTabFile;
  // The default realm to use for Kerberos authentication.
  private String realm;
  // The fully-qualified DNS name to use for the Directory Server system.  This
  // is factored into the authentication process.
  // The fully-qualified domain name for the server system.
  private String serverFQDN;
@@ -110,7 +93,6 @@
  public GSSAPISASLMechanismHandler()
  {
    super();
  }
@@ -119,183 +101,50 @@
   * {@inheritDoc}
   */
  @Override()
  public void initializeSASLMechanismHandler(ConfigEntry configEntry)
  public void initializeSASLMechanismHandler(
                   GSSAPISASLMechanismHandlerCfg configuration)
         throws ConfigException, InitializationException
  {
    this.configEntryDN = configEntry.getDN();
    configuration.addGSSAPIChangeListener(this);
    currentConfig = configuration;
    configEntryDN = configuration.dn();
    // Get the identity mapper that should be used to find users.
    int msgID = MSGID_SASLGSSAPI_DESCRIPTION_IDENTITY_MAPPER_DN;
    DNConfigAttribute mapperStub =
         new DNConfigAttribute(ATTR_IDMAPPER_DN, getMessage(msgID), true, false,
                               false);
    try
    DN identityMapperDN = configuration.getIdentityMapperDN();
    identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
    if (identityMapper == null)
    {
      DNConfigAttribute mapperAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(mapperStub);
      if (mapperAttr == null)
      {
        msgID = MSGID_SASLGSSAPI_NO_IDENTITY_MAPPER_ATTR;
        String message = getMessage(msgID, String.valueOf(configEntryDN));
        throw new ConfigException(msgID, message);
      }
      else
      {
        identityMapperDN = mapperAttr.activeValue();
        identityMapper = DirectoryServer.getIdentityMapper(identityMapperDN);
        if (identityMapper == null)
        {
          msgID = MSGID_SASLGSSAPI_NO_SUCH_IDENTITY_MAPPER;
          String message = getMessage(msgID, String.valueOf(identityMapperDN),
                                      String.valueOf(configEntryDN));
          throw new ConfigException(msgID, message);
        }
      }
    }
    catch (ConfigException ce)
    {
      throw ce;
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_IDENTITY_MAPPER;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
      int    msgID   = MSGID_SASLGSSAPI_NO_SUCH_IDENTITY_MAPPER;
      String message = getMessage(msgID, String.valueOf(identityMapperDN),
                                  String.valueOf(configEntryDN));
      throw new ConfigException(msgID, message);
    }
    // Determine the fully-qualified hostname for this system.  It may be
    // provided, but if not, then try to determine it programmatically.
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_SERVER_FQDN;
    StringConfigAttribute serverFQDNStub =
         new StringConfigAttribute(ATTR_SERVER_FQDN, getMessage(msgID), false,
                                false, false);
    try
    serverFQDN = configuration.getServerFqdn();
    if (serverFQDN == null)
    {
      StringConfigAttribute serverFQDNAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(serverFQDNStub);
      if (serverFQDNAttr == null)
      try
      {
        // No value was provided, so try to figure it out for ourselves.
        serverFQDN = InetAddress.getLocalHost().getCanonicalHostName();
      }
      else
      catch (Exception e)
      {
        serverFQDN = serverFQDNAttr.activeValue();
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        int    msgID   = MSGID_SASLGSSAPI_CANNOT_GET_SERVER_FQDN;
        String message = getMessage(msgID, String.valueOf(configEntryDN),
                                    stackTraceToSingleLineString(e));
        throw new InitializationException(msgID, message, e);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_SERVER_FQDN;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the address of the KDC to use.  If it is not provided, then
    // we'll assume that the underlying OS has a valid config file.
    kdcAddress = null;
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_KDC_ADDRESS;
    StringConfigAttribute kdcStub =
         new StringConfigAttribute(ATTR_GSSAPI_KDC, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute kdcAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(kdcStub);
      if (kdcAttr != null)
      {
        kdcAddress = kdcAttr.activeValue();
        System.setProperty(KRBV_PROPERTY_KDC, kdcAddress);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_KDC_ADDRESS;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the default realm to use.  If it is not provided, then we'll
    // assume that the underlying OS has a valid config file.
    realm = null;
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_REALM;
    StringConfigAttribute realmStub =
         new StringConfigAttribute(ATTR_GSSAPI_REALM, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute realmAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(realmStub);
      if (realmAttr != null)
      {
        realm = realmAttr.activeValue();
        System.setProperty(KRBV_PROPERTY_REALM, realm);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_REALM;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Determine the path of the keytab file to use.  If it is not provided,
    // then we'll let Java use the system default keytab.
    keyTabFile = null;
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_KEYTAB_FILE;
    StringConfigAttribute keyTabStub =
         new StringConfigAttribute(ATTR_GSSAPI_KEYTAB_FILE, getMessage(msgID),
                                   false, false, false);
    try
    {
      StringConfigAttribute keyTabAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(keyTabStub);
      if (keyTabAttr != null)
      {
        keyTabFile = keyTabAttr.activeValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_KEYTAB_FILE;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
    // Since we're going to be using JAAS behind the scenes, we need to have a
@@ -316,6 +165,7 @@
      w.write("  com.sun.security.auth.module.Krb5LoginModule required " +
              "storeKey=true useKeyTab=true ");
      String keyTabFile = configuration.getKeytab();
      if (keyTabFile != null)
      {
        w.write("keyTab=\"" + keyTabFile + "\" ");
@@ -325,6 +175,8 @@
      // FIXME -- Can we get away from hard-coding a protocol here?
      w.write("principal=\"ldap/" + serverFQDN);
      String realm = configuration.getRealm();
      if (realm != null)
      {
        w.write("@" + realm);
@@ -341,7 +193,12 @@
    }
    catch (Exception e)
    {
      msgID = MSGID_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG;
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      int    msgID   = MSGID_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG;
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new InitializationException(msgID, message, e);
    }
@@ -351,7 +208,6 @@
    DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_GSSAPI, this);
    DirectoryServer.registerConfigurableComponent(this);
  }
@@ -362,7 +218,7 @@
  @Override()
  public void finalizeSASLMechanismHandler()
  {
    DirectoryServer.deregisterConfigurableComponent(this);
    currentConfig.removeGSSAPIChangeListener(this);
    DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_GSSAPI);
  }
@@ -489,526 +345,6 @@
  /**
   * Retrieves the DN of the configuration entry with which this component is
   * associated.
   *
   * @return  The DN of the configuration entry with which this component is
   *          associated.
   */
  public DN getConfigurableComponentEntryDN()
  {
    return configEntryDN;
  }
  /**
   * Retrieves the set of configuration attributes that are associated with this
   * configurable component.
   *
   * @return  The set of configuration attributes that are associated with this
   *          configurable component.
   */
  public List<ConfigAttribute> getConfigurationAttributes()
  {
    LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>();
    int msgID = MSGID_SASLGSSAPI_DESCRIPTION_IDENTITY_MAPPER_DN;
    attrList.add(new DNConfigAttribute(ATTR_IDMAPPER_DN, getMessage(msgID),
                                       true, false, false, identityMapperDN));
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_SERVER_FQDN;
    attrList.add(new StringConfigAttribute(ATTR_SERVER_FQDN, getMessage(msgID),
                                           false, false, false, serverFQDN));
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_KDC_ADDRESS;
    attrList.add(new StringConfigAttribute(ATTR_GSSAPI_KDC, getMessage(msgID),
                                           false, false, false, kdcAddress));
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_REALM;
    attrList.add(new StringConfigAttribute(ATTR_GSSAPI_REALM, getMessage(msgID),
                                           false, false, false, realm));
    return attrList;
  }
  /**
   * Indicates whether the provided configuration entry has an acceptable
   * configuration for this component.  If it does not, then detailed
   * information about the problem(s) should be added to the provided list.
   *
   * @param  configEntry          The configuration entry for which to make the
   *                              determination.
   * @param  unacceptableReasons  A list that can be used to hold messages about
   *                              why the provided entry does not have an
   *                              acceptable configuration.
   *
   * @return  <CODE>true</CODE> if the provided entry has an acceptable
   *          configuration for this component, or <CODE>false</CODE> if not.
   */
  public boolean hasAcceptableConfiguration(ConfigEntry configEntry,
                                            List<String> unacceptableReasons)
  {
    // Look at the identity mapper configuration
    int msgID = MSGID_SASLGSSAPI_DESCRIPTION_IDENTITY_MAPPER_DN;
    DNConfigAttribute mapperStub =
         new DNConfigAttribute(ATTR_IDMAPPER_DN, getMessage(msgID), true, false,
                               false);
    try
    {
      DNConfigAttribute mapperAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(mapperStub);
      if (mapperAttr == null)
      {
        msgID = MSGID_SASLGSSAPI_NO_IDENTITY_MAPPER_ATTR;
        String message = getMessage(msgID, String.valueOf(configEntryDN));
        unacceptableReasons.add(message);
        return false;
      }
      else
      {
        DN mapperDN = mapperAttr.activeValue();
        IdentityMapper mapper =
             DirectoryServer.getIdentityMapper(mapperDN);
        if (mapper == null)
        {
          msgID = MSGID_SASLGSSAPI_NO_SUCH_IDENTITY_MAPPER;
          String message = getMessage(msgID, String.valueOf(mapperDN),
                                      String.valueOf(configEntryDN));
          unacceptableReasons.add(message);
          return false;
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_IDENTITY_MAPPER;
      String message = getMessage(msgID, String.valueOf(configEntryDN),
                                  stackTraceToSingleLineString(e));
      unacceptableReasons.add(message);
      return false;
    }
    // Look a the server FQDN configuration.
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_SERVER_FQDN;
    StringConfigAttribute serverFQDNStub  =
         new StringConfigAttribute(ATTR_SERVER_FQDN, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute serverFQDNAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(serverFQDNStub);
      // FIXME -- Should we try to resolve the value if one is provided?
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_SERVER_FQDN;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      return false;
    }
    // Look at the KDC configuration.
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_KDC_ADDRESS;
    StringConfigAttribute kdcStub =
         new StringConfigAttribute(ATTR_GSSAPI_KDC, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute kdcAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(kdcStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_KDC_ADDRESS;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      return false;
    }
    // Look at the realm configuration.
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_REALM;
    StringConfigAttribute realmStub =
         new StringConfigAttribute(ATTR_GSSAPI_REALM, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute realmAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(realmStub);
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_REALM;
      unacceptableReasons.add(getMessage(msgID, String.valueOf(configEntryDN),
                                         stackTraceToSingleLineString(e)));
      return false;
    }
    // If we've gotten to this point, then everything must be OK.
    return true;
  }
  /**
   * Makes a best-effort attempt to apply the configuration contained in the
   * provided entry.  Information about the result of this processing should be
   * added to the provided message list.  Information should always be added to
   * this list if a configuration change could not be applied.  If detailed
   * results are requested, then information about the changes applied
   * successfully (and optionally about parameters that were not changed) should
   * also be included.
   *
   * @param  configEntry      The entry containing the new configuration to
   *                          apply for this component.
   * @param  detailedResults  Indicates whether detailed information about the
   *                          processing should be added to the list.
   *
   * @return  Information about the result of the configuration update.
   */
  public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry,
                                                  boolean detailedResults)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Look at the identity mapper configuration
    DN             newIdentityMapperDN = null;
    IdentityMapper newIdentityMapper   = null;
    int msgID = MSGID_SASLGSSAPI_DESCRIPTION_IDENTITY_MAPPER_DN;
    DNConfigAttribute mapperStub =
         new DNConfigAttribute(ATTR_IDMAPPER_DN, getMessage(msgID), true, false,
                               false);
    try
    {
      DNConfigAttribute mapperAttr =
           (DNConfigAttribute) configEntry.getConfigAttribute(mapperStub);
      if (mapperAttr == null)
      {
        msgID = MSGID_SASLGSSAPI_NO_IDENTITY_MAPPER_ATTR;
        messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = ResultCode.OBJECTCLASS_VIOLATION;
        }
      }
      else
      {
        newIdentityMapperDN = mapperAttr.activeValue();
        newIdentityMapper =
             DirectoryServer.getIdentityMapper(newIdentityMapperDN);
        if (newIdentityMapper == null)
        {
          msgID = MSGID_SASLGSSAPI_NO_SUCH_IDENTITY_MAPPER;
          messages.add(getMessage(msgID, String.valueOf(newIdentityMapperDN),
                                  String.valueOf(configEntryDN)));
          if (resultCode == ResultCode.SUCCESS)
          {
            resultCode = ResultCode.CONSTRAINT_VIOLATION;
          }
        }
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_IDENTITY_MAPPER;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // Look at the server FQDN configuration.
    String newServerFQDN = null;
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_SERVER_FQDN;
    StringConfigAttribute serverFQDNStub  =
         new StringConfigAttribute(ATTR_SERVER_FQDN, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute serverFQDNAttr =
           (StringConfigAttribute)
           configEntry.getConfigAttribute(serverFQDNStub);
      if (serverFQDNAttr == null)
      {
        newServerFQDN = InetAddress.getLocalHost().getCanonicalHostName();
      }
      else
      {
        newServerFQDN = serverFQDNAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_SERVER_FQDN;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // Look at the KDC configuration.
    String newKDC = null;
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_KDC_ADDRESS;
    StringConfigAttribute kdcStub =
         new StringConfigAttribute(ATTR_GSSAPI_KDC, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute kdcAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(kdcStub);
      if (kdcAttr != null)
      {
        newKDC = kdcAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_KDC_ADDRESS;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // Look at the realm configuration.
    String newRealm = null;
    msgID = MSGID_SASLGSSAPI_DESCRIPTION_REALM;
    StringConfigAttribute realmStub =
         new StringConfigAttribute(ATTR_GSSAPI_REALM, getMessage(msgID), false,
                                   false, false);
    try
    {
      StringConfigAttribute realmAttr =
           (StringConfigAttribute) configEntry.getConfigAttribute(realmStub);
      if (realmAttr != null)
      {
        newRealm = realmAttr.pendingValue();
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      msgID = MSGID_SASLGSSAPI_CANNOT_GET_REALM;
      messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                              stackTraceToSingleLineString(e)));
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    // If everything has been successful, then apply any changes that were made.
    if (resultCode == ResultCode.SUCCESS)
    {
      if (! identityMapperDN.equals(newIdentityMapperDN))
      {
        identityMapperDN = newIdentityMapperDN;
        identityMapper   = newIdentityMapper;
        if (detailedResults)
        {
          msgID = MSGID_SASLGSSAPI_UPDATED_IDENTITY_MAPPER;
          messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                  String.valueOf(identityMapperDN)));
        }
      }
      if (serverFQDN == null)
      {
        if (newServerFQDN != null)
        {
          serverFQDN = newServerFQDN;
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UPDATED_NEW_SERVER_FQDN;
            messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                    String.valueOf(serverFQDN)));
          }
        }
      }
      else if (newServerFQDN == null)
      {
        serverFQDN = null;
        if (detailedResults)
        {
          msgID = MSGID_SASLGSSAPI_UPDATED_NO_SERVER_FQDN;
          messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
        }
      }
      else
      {
        if (! serverFQDN.equals(newServerFQDN))
        {
          serverFQDN = newServerFQDN;
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UPDATED_NEW_SERVER_FQDN;
            messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                    String.valueOf(serverFQDN)));
          }
        }
      }
      if (kdcAddress == null)
      {
        if (newKDC != null)
        {
          kdcAddress = newKDC;
          System.setProperty(KRBV_PROPERTY_KDC, kdcAddress);
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UPDATED_KDC;
            messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                    String.valueOf(kdcAddress)));
          }
        }
      }
      else
      {
        if (newKDC == null)
        {
          kdcAddress = null;
          System.clearProperty(KRBV_PROPERTY_KDC);
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UNSET_KDC;
            messages.add(getMessage(msgID, String.valueOf(configEntryDN)));
          }
        }
        else if (! kdcAddress.equals(newKDC))
        {
          kdcAddress = newKDC;
          System.setProperty(KRBV_PROPERTY_KDC, kdcAddress);
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UPDATED_KDC;
            messages.add(getMessage(msgID, String.valueOf(kdcAddress)));
          }
        }
      }
      if (realm == null)
      {
        if (newRealm != null)
        {
          realm = newRealm;
          System.setProperty(KRBV_PROPERTY_REALM, realm);
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UPDATED_REALM;
            messages.add(getMessage(msgID, String.valueOf(realm)));
          }
        }
      }
      else
      {
        if (newRealm == null)
        {
          realm = null;
          System.clearProperty(KRBV_PROPERTY_REALM);
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UNSET_REALM;
            messages.add(getMessage(msgID));
          }
        }
        else if (! realm.equals(newRealm))
        {
          realm = newRealm;
          System.setProperty(KRBV_PROPERTY_REALM, realm);
          if (detailedResults)
          {
            msgID = MSGID_SASLGSSAPI_UPDATED_REALM;
            messages.add(getMessage(msgID, String.valueOf(realm)));
          }
        }
      }
    }
    // Return the result to the caller.
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
@@ -1029,5 +365,159 @@
    // This may be considered a secure mechanism.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
                      GSSAPISASLMechanismHandlerCfg configuration,
                      List<String> unacceptableReasons)
  {
    boolean configAcceptable = true;
    // Get the identity mapper that should be used to find users.
    DN identityMapperDN = configuration.getIdentityMapperDN();
    IdentityMapper newIdentityMapper =
         DirectoryServer.getIdentityMapper(identityMapperDN);
    if (newIdentityMapper == null)
    {
      int msgID = MSGID_SASLGSSAPI_NO_SUCH_IDENTITY_MAPPER;
      unacceptableReasons.add(getMessage(msgID,
                                         String.valueOf(identityMapperDN),
                                         String.valueOf(configEntryDN)));
      configAcceptable = false;
    }
    return configAcceptable;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
              GSSAPISASLMechanismHandlerCfg configuration)
  {
    ResultCode        resultCode          = ResultCode.SUCCESS;
    boolean           adminActionRequired = false;
    ArrayList<String> messages            = new ArrayList<String>();
    // Get the identity mapper that should be used to find users.
    DN identityMapperDN = configuration.getIdentityMapperDN();
    IdentityMapper newIdentityMapper =
         DirectoryServer.getIdentityMapper(identityMapperDN);
    if (newIdentityMapper == null)
    {
      if (resultCode == ResultCode.SUCCESS)
      {
        resultCode = ResultCode.CONSTRAINT_VIOLATION;
      }
      int msgID = MSGID_SASLGSSAPI_NO_SUCH_IDENTITY_MAPPER;
      messages.add(getMessage(msgID, String.valueOf(identityMapperDN),
                              String.valueOf(configEntryDN)));
    }
    // Determine the fully-qualified hostname for this system.  It may be
    // provided, but if not, then try to determine it programmatically.
    String newFQDN = configuration.getServerFqdn();
    if (newFQDN == null)
    {
      try
      {
        newFQDN = InetAddress.getLocalHost().getCanonicalHostName();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        if (resultCode == ResultCode.SUCCESS)
        {
          resultCode = DirectoryServer.getServerErrorResultCode();
        }
        int msgID = MSGID_SASLGSSAPI_CANNOT_GET_SERVER_FQDN;
        messages.add(getMessage(msgID, String.valueOf(configEntryDN),
                                stackTraceToSingleLineString(e)));
      }
    }
    if (resultCode == ResultCode.SUCCESS)
    {
      String configFileName;
      try
      {
        File tempFile = File.createTempFile("login", "conf");
        configFileName = tempFile.getAbsolutePath();
        tempFile.deleteOnExit();
        BufferedWriter w = new BufferedWriter(new FileWriter(tempFile, false));
        w.write(getClass().getName() + " {");
        w.newLine();
        w.write("  com.sun.security.auth.module.Krb5LoginModule required " +
                "storeKey=true useKeyTab=true ");
        String keyTabFile = configuration.getKeytab();
        if (keyTabFile != null)
        {
          w.write("keyTab=\"" + keyTabFile + "\" ");
        }
        // FIXME -- Should we add the ability to include "debug=true"?
        // FIXME -- Can we get away from hard-coding a protocol here?
        w.write("principal=\"ldap/" + serverFQDN);
        String realm = configuration.getRealm();
        if (realm != null)
        {
          w.write("@" + realm);
        }
        w.write("\";");
        w.newLine();
        w.write("};");
        w.newLine();
        w.flush();
        w.close();
      }
      catch (Exception e)
      {
        if (debugEnabled())
        {
          debugCaught(DebugLogLevel.ERROR, e);
        }
        resultCode = DirectoryServer.getServerErrorResultCode();
        int msgID = MSGID_SASLGSSAPI_CANNOT_CREATE_JAAS_CONFIG;
        messages.add(getMessage(msgID, stackTraceToSingleLineString(e)));
       return new ConfigChangeResult(resultCode, adminActionRequired, messages);
      }
      System.setProperty(JAAS_PROPERTY_CONFIG_FILE, configFileName);
      identityMapper = newIdentityMapper;
      serverFQDN     = newFQDN;
      currentConfig  = configuration;
    }
   return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
}