001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2007-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.server.core;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.forgerock.i18n.LocalizableMessage;
023import org.forgerock.i18n.LocalizedIllegalArgumentException;
024import org.forgerock.i18n.slf4j.LocalizedLogger;
025import org.forgerock.opendj.ldap.ByteString;
026import org.forgerock.opendj.ldap.DN;
027import org.forgerock.opendj.ldap.ResultCode;
028import org.opends.server.api.ClientConnection;
029import org.opends.server.types.*;
030import org.opends.server.types.operation.PreParseBindOperation;
031import org.opends.server.workflowelement.localbackend.LocalBackendBindOperation;
032
033import static org.forgerock.opendj.ldap.ResultCode.*;
034import static org.opends.messages.CoreMessages.*;
035import static org.opends.server.core.DirectoryServer.*;
036import static org.opends.server.loggers.AccessLogger.*;
037import static org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement.*;
038
039/**
040 * This class defines an operation that may be used to authenticate a user to
041 * the Directory Server.  Note that for security restrictions, response messages
042 * that may be returned to the client must be carefully cleaned to ensure that
043 * they do not provide a malicious client with information that may be useful in
044 * an attack.  This does impact the debuggability of the server, but that can
045 * be addressed by calling the <CODE>setAuthFailureReason</CODE> method, which
046 * can provide a reason for a failure in a form that will not be returned to the
047 * client but may be written to a log file.
048 */
049public class BindOperationBasis
050             extends AbstractOperation
051             implements BindOperation, PreParseBindOperation
052{
053  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
054
055  /** The credentials used for SASL authentication. */
056  private ByteString saslCredentials;
057
058  /** The server SASL credentials provided to the client in the response. */
059  private ByteString serverSASLCredentials;
060
061  /** The authentication info for this bind operation. */
062  private AuthenticationInfo authInfo;
063
064  /** The authentication type used for this bind operation. */
065  private AuthenticationType authType;
066
067  /** The raw, unprocessed bind DN as contained in the client request. */
068  private ByteString rawBindDN;
069
070  /** The password used for simple authentication. */
071  private ByteString simplePassword;
072
073  /** The bind DN used for this bind operation. */
074  private DN bindDN;
075
076  /** The DN of the user entry that is attempting to authenticate. */
077  private DN userEntryDN;
078
079  /**
080   * The DN of the user as whom a SASL authentication was attempted (regardless
081   * of whether the authentication was successful) for the purpose of updating
082   * password policy state information.
083   */
084  private Entry saslAuthUserEntry;
085
086  /** The set of response controls for this bind operation. */
087  private final List<Control> responseControls = new ArrayList<>(0);
088
089  /** A message explaining the reason for the authentication failure. */
090  private LocalizableMessage authFailureReason;
091
092  /** The SASL mechanism used for SASL authentication. */
093  private String saslMechanism;
094
095  /** A string representation of the protocol version for this bind operation. */
096  private String protocolVersion;
097
098  /**
099   * Creates a new simple bind operation with the provided information.
100   *
101   * @param  clientConnection  The client connection with which this operation
102   *                           is associated.
103   * @param  operationID       The operation ID for this operation.
104   * @param  messageID         The message ID of the request with which this
105   *                           operation is associated.
106   * @param  requestControls   The set of controls included in the request.
107   * @param  protocolVersion   The string representation of the protocol version
108   *                           associated with this bind request.
109   * @param  rawBindDN         The raw, unprocessed bind DN as provided in the
110   *                           request from the client.
111   * @param  simplePassword    The password to use for the simple
112   *                           authentication.
113   */
114  public BindOperationBasis(ClientConnection clientConnection, long operationID,
115                       int messageID, List<Control> requestControls,
116                       String protocolVersion, ByteString rawBindDN,
117                       ByteString simplePassword)
118  {
119    super(clientConnection, operationID, messageID, requestControls);
120
121    this.protocolVersion = protocolVersion;
122
123    setRawBindDN(rawBindDN);
124    setSimplePassword(simplePassword);
125
126    cancelResult = getBindCancelResult();
127  }
128
129  /**
130   * Creates a new SASL bind operation with the provided information.
131   *
132   * @param  clientConnection  The client connection with which this operation
133   *                           is associated.
134   * @param  operationID       The operation ID for this operation.
135   * @param  messageID         The message ID of the request with which this
136   *                           operation is associated.
137   * @param  requestControls   The set of controls included in the request.
138   * @param  protocolVersion   The string representation of the protocol version
139   *                           associated with this bind request.
140   * @param  rawBindDN         The raw, unprocessed bind DN as provided in the
141   *                           request from the client.
142   * @param  saslMechanism     The SASL mechanism included in the request.
143   * @param  saslCredentials   The optional SASL credentials included in the
144   *                           request.
145   */
146  public BindOperationBasis(ClientConnection clientConnection, long operationID,
147                       int messageID, List<Control> requestControls,
148                       String protocolVersion, ByteString rawBindDN,
149                       String saslMechanism, ByteString saslCredentials)
150  {
151    super(clientConnection, operationID, messageID, requestControls);
152
153    this.protocolVersion = protocolVersion;
154    this.authType        = AuthenticationType.SASL;
155    this.saslMechanism   = saslMechanism;
156    this.saslCredentials = saslCredentials;
157
158    setRawBindDN(rawBindDN);
159
160    cancelResult = getBindCancelResult();
161  }
162
163  /**
164   * Creates a new simple bind operation with the provided information.
165   *
166   * @param  clientConnection  The client connection with which this operation
167   *                           is associated.
168   * @param  operationID       The operation ID for this operation.
169   * @param  messageID         The message ID of the request with which this
170   *                           operation is associated.
171   * @param  requestControls   The set of controls included in the request.
172   * @param  protocolVersion   The string representation of the protocol version
173   *                           associated with this bind request.
174   * @param  bindDN            The bind DN for this bind operation.
175   * @param  simplePassword    The password to use for the simple
176   *                           authentication.
177   */
178  public BindOperationBasis(ClientConnection clientConnection, long operationID,
179                       int messageID, List<Control> requestControls,
180                       String protocolVersion, DN bindDN,
181                       ByteString simplePassword)
182  {
183    super(clientConnection, operationID, messageID, requestControls);
184
185    this.protocolVersion = protocolVersion;
186    this.bindDN          = bindDN;
187
188    rawBindDN = computeRawBindDN(bindDN);
189
190    setSimplePassword(simplePassword);
191
192    cancelResult = getBindCancelResult();
193  }
194
195  /**
196   * Creates a new SASL bind operation with the provided information.
197   *
198   * @param  clientConnection  The client connection with which this operation
199   *                           is associated.
200   * @param  operationID       The operation ID for this operation.
201   * @param  messageID         The message ID of the request with which this
202   *                           operation is associated.
203   * @param  requestControls   The set of controls included in the request.
204   * @param  protocolVersion   The string representation of the protocol version
205   *                           associated with this bind request.
206   * @param  bindDN            The bind DN for this bind operation.
207   * @param  saslMechanism     The SASL mechanism included in the request.
208   * @param  saslCredentials   The optional SASL credentials included in the
209   *                           request.
210   */
211  public BindOperationBasis(ClientConnection clientConnection, long operationID,
212                       int messageID, List<Control> requestControls,
213                       String protocolVersion, DN bindDN,
214                       String saslMechanism, ByteString saslCredentials)
215  {
216    super(clientConnection, operationID, messageID, requestControls);
217
218    this.protocolVersion = protocolVersion;
219    this.authType        = AuthenticationType.SASL;
220    this.bindDN          = bindDN;
221    this.saslMechanism   = saslMechanism;
222    this.saslCredentials = saslCredentials;
223
224    rawBindDN = computeRawBindDN(bindDN);
225
226    cancelResult = getBindCancelResult();
227  }
228
229  private ByteString computeRawBindDN(DN bindDN)
230  {
231    if (bindDN != null)
232    {
233      return ByteString.valueOfUtf8(bindDN.toString());
234    }
235    return ByteString.empty();
236  }
237
238  private CancelResult getBindCancelResult()
239  {
240    return new CancelResult(CANNOT_CANCEL, ERR_CANNOT_CANCEL_BIND.get());
241  }
242
243  @Override
244  public DN getProxiedAuthorizationDN()
245  {
246    return null;
247  }
248
249  @Override
250  public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN)
251  {
252  }
253
254  @Override
255  public final AuthenticationType getAuthenticationType()
256  {
257    return authType;
258  }
259
260  @Override
261  public final ByteString getRawBindDN()
262  {
263    return rawBindDN;
264  }
265
266  @Override
267  public final void setRawBindDN(ByteString rawBindDN)
268  {
269    if (rawBindDN != null)
270    {
271      this.rawBindDN = rawBindDN;
272    }
273    else
274    {
275      this.rawBindDN = ByteString.empty();
276    }
277
278    bindDN = null;
279  }
280
281  @Override
282  public final DN getBindDN()
283  {
284    try
285    {
286      if (bindDN == null)
287      {
288        bindDN = DN.valueOf(rawBindDN);
289      }
290    }
291    catch (LocalizedIllegalArgumentException e)
292    {
293      logger.traceException(e);
294
295      setResultCode(ResultCode.INVALID_CREDENTIALS);
296      setAuthFailureReason(e.getMessageObject());
297    }
298    return bindDN;
299  }
300
301  @Override
302  public final ByteString getSimplePassword()
303  {
304    return simplePassword;
305  }
306
307  @Override
308  public final void setSimplePassword(ByteString simplePassword)
309  {
310    if (simplePassword != null)
311    {
312      this.simplePassword = simplePassword;
313    }
314    else
315    {
316      this.simplePassword = ByteString.empty();
317    }
318
319    authType        = AuthenticationType.SIMPLE;
320    saslMechanism   = null;
321    saslCredentials = null;
322  }
323
324  @Override
325  public final String getSASLMechanism()
326  {
327    return saslMechanism;
328  }
329
330  @Override
331  public final ByteString getSASLCredentials()
332  {
333    return saslCredentials;
334  }
335
336  @Override
337  public final void setSASLCredentials(String saslMechanism,
338                                       ByteString saslCredentials)
339  {
340    this.saslMechanism   = saslMechanism;
341    this.saslCredentials = saslCredentials;
342
343    authType       = AuthenticationType.SASL;
344    simplePassword = null;
345  }
346
347  @Override
348  public final ByteString getServerSASLCredentials()
349  {
350    return serverSASLCredentials;
351  }
352
353  @Override
354  public final void setServerSASLCredentials(ByteString serverSASLCredentials)
355  {
356    this.serverSASLCredentials = serverSASLCredentials;
357  }
358
359  @Override
360  public final Entry getSASLAuthUserEntry()
361  {
362    return saslAuthUserEntry;
363  }
364
365  @Override
366  public final void setSASLAuthUserEntry(Entry saslAuthUserEntry)
367  {
368    this.saslAuthUserEntry = saslAuthUserEntry;
369  }
370
371  @Override
372  public final LocalizableMessage getAuthFailureReason()
373  {
374    return authFailureReason;
375  }
376
377  @Override
378  public final void setAuthFailureReason(LocalizableMessage message)
379  {
380    if (DirectoryServer.returnBindErrorMessages())
381    {
382      appendErrorMessage(message);
383    }
384    else
385    {
386      authFailureReason = message;
387    }
388  }
389
390  @Override
391  public final DN getUserEntryDN()
392  {
393    return userEntryDN;
394  }
395
396  @Override
397  public final AuthenticationInfo getAuthenticationInfo()
398  {
399    return authInfo;
400  }
401
402  @Override
403  public final void setAuthenticationInfo(AuthenticationInfo authInfo)
404  {
405    this.authInfo = authInfo;
406  }
407
408  @Override
409  public final OperationType getOperationType()
410  {
411    // Note that no debugging will be done in this method because it is a likely
412    // candidate for being called by the logging subsystem.
413    return OperationType.BIND;
414  }
415
416  @Override
417  public final List<Control> getResponseControls()
418  {
419    return responseControls;
420  }
421
422  @Override
423  public final void addResponseControl(Control control)
424  {
425    responseControls.add(control);
426  }
427
428  @Override
429  public final void removeResponseControl(Control control)
430  {
431    responseControls.remove(control);
432  }
433
434  @Override
435  public final void toString(StringBuilder buffer)
436  {
437    buffer.append("BindOperation(connID=");
438    buffer.append(clientConnection.getConnectionID());
439    buffer.append(", opID=");
440    buffer.append(operationID);
441    buffer.append(", protocol=\"");
442    buffer.append(clientConnection.getProtocol());
443    buffer.append(" ");
444    buffer.append(protocolVersion);
445    buffer.append(", dn=");
446    buffer.append(rawBindDN);
447    buffer.append(", authType=");
448    buffer.append(authType);
449    buffer.append(")");
450  }
451
452  @Override
453  public void setUserEntryDN(DN userEntryDN)
454  {
455    this.userEntryDN = userEntryDN;
456  }
457
458  @Override
459  public String getProtocolVersion()
460  {
461    return protocolVersion;
462  }
463
464  @Override
465  public void setProtocolVersion(String protocolVersion)
466  {
467    this.protocolVersion = protocolVersion;
468  }
469
470  @Override
471  public final void run()
472  {
473    // Start the processing timer and initially set the result to indicate that
474    // the result is unknown.
475    setResultCode(ResultCode.UNDEFINED);
476    setProcessingStartTime();
477
478    logBindRequest(this);
479
480    // Wipe out any existing authentication for the client connection and create
481    // a placeholder that will be used if the bind is successful.
482    ClientConnection clientConnection = getClientConnection();
483    clientConnection.setUnauthenticated();
484
485    // Abandon any operations that may be in progress for the client.
486    LocalizableMessage cancelReason = INFO_CANCELED_BY_BIND_REQUEST.get();
487    CancelRequest cancelRequest = new CancelRequest(true, cancelReason);
488    clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID());
489
490    // This flag is set to true as soon as a workflow has been executed.
491    boolean workflowExecuted = false;
492    try
493    {
494      // Invoke the pre-parse bind plugins.
495      if (!processOperationResult(getPluginConfigManager().invokePreParseBindPlugins(this)))
496      {
497        return;
498      }
499
500      // Process the bind DN to convert it from the raw form as provided by the
501      // client into the form required for the rest of the bind processing.
502      DN bindDN = getBindDN();
503      if (bindDN == null){
504        return;
505      }
506
507      // If this is a simple bind
508      // Then check whether the bind DN is actually one of the alternate root DNs
509      // defined in the server.  If so, then replace it with the actual DN
510      // for that user.
511      switch (getAuthenticationType())
512      {
513        case SIMPLE:
514          DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN);
515          if (actualRootDN != null)
516          {
517            bindDN = actualRootDN;
518          }
519      }
520
521      workflowExecuted = execute(this, bindDN);
522    }
523    catch(CanceledOperationException coe)
524    {
525      // This shouldn't happen for bind operations. Just cancel anyways
526      logger.traceException(coe);
527
528      setResultCode(ResultCode.CANCELLED);
529
530      appendErrorMessage(cancelRequest.getCancelReason());
531    }
532    finally
533    {
534      setProcessingStopTime();
535      logBindResponse(this);
536
537      // Send the bind response to the client.
538      clientConnection.sendResponse(this);
539
540      // If the bind processing is finished, then unset the "bind in progress"
541      // flag to allow other operations to be processed on the connection.
542      if (getResultCode() != ResultCode.SASL_BIND_IN_PROGRESS)
543      {
544        clientConnection.finishSaslBind();
545      }
546      clientConnection.finishBind();
547
548      invokePostResponsePlugins(workflowExecuted);
549    }
550  }
551
552  /**
553   * Invokes the post response plugins. If a workflow has been executed
554   * then invoke the post response plugins provided by the workflow
555   * elements of the workflow, otherwise invoke the post response plugins
556   * that have been registered with the current operation.
557   *
558   * @param workflowExecuted <code>true</code> if a workflow has been executed
559   */
560  private void invokePostResponsePlugins(boolean workflowExecuted)
561  {
562    // Invoke the post response plugins
563    if (workflowExecuted)
564    {
565      // The post responses are provided by the workflow elements of the workflow.
566      List localOperations = (List) getAttachment(Operation.LOCALBACKENDOPERATIONS);
567      if (localOperations != null)
568      {
569        for (Object localOp : localOperations)
570        {
571          LocalBackendBindOperation localOperation = (LocalBackendBindOperation) localOp;
572          // Invoke the post-response bind plugins.
573          getPluginConfigManager().invokePostResponseBindPlugins(localOperation);
574        }
575      }
576      else
577      {
578        // The current operation does not implement any bind post response
579        // interface so we cannot invoke any post-response plugin.
580      }
581    }
582  }
583
584  @Override
585  public void updateOperationErrMsgAndResCode()
586  {
587    LocalizableMessage message = ERR_BIND_OPERATION_UNKNOWN_USER.get();
588    setResultCode(ResultCode.INVALID_CREDENTIALS);
589    setAuthFailureReason(message);
590  }
591}