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