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 /** 096 * A string representation of the protocol version for this bind operation. 097 */ 098 private String protocolVersion; 099 100 /** 101 * Creates a new simple bind operation with the provided information. 102 * 103 * @param clientConnection The client connection with which this operation 104 * is associated. 105 * @param operationID The operation ID for this operation. 106 * @param messageID The message ID of the request with which this 107 * operation is associated. 108 * @param requestControls The set of controls included in the request. 109 * @param protocolVersion The string representation of the protocol version 110 * associated with this bind request. 111 * @param rawBindDN The raw, unprocessed bind DN as provided in the 112 * request from the client. 113 * @param simplePassword The password to use for the simple 114 * authentication. 115 */ 116 public BindOperationBasis(ClientConnection clientConnection, long operationID, 117 int messageID, List<Control> requestControls, 118 String protocolVersion, ByteString rawBindDN, 119 ByteString simplePassword) 120 { 121 super(clientConnection, operationID, messageID, requestControls); 122 123 this.protocolVersion = protocolVersion; 124 125 setRawBindDN(rawBindDN); 126 setSimplePassword(simplePassword); 127 128 cancelResult = getBindCancelResult(); 129 } 130 131 132 133 /** 134 * Creates a new SASL bind operation with the provided information. 135 * 136 * @param clientConnection The client connection with which this operation 137 * is associated. 138 * @param operationID The operation ID for this operation. 139 * @param messageID The message ID of the request with which this 140 * operation is associated. 141 * @param requestControls The set of controls included in the request. 142 * @param protocolVersion The string representation of the protocol version 143 * associated with this bind request. 144 * @param rawBindDN The raw, unprocessed bind DN as provided in the 145 * request from the client. 146 * @param saslMechanism The SASL mechanism included in the request. 147 * @param saslCredentials The optional SASL credentials included in the 148 * request. 149 */ 150 public BindOperationBasis(ClientConnection clientConnection, long operationID, 151 int messageID, List<Control> requestControls, 152 String protocolVersion, ByteString rawBindDN, 153 String saslMechanism, ByteString saslCredentials) 154 { 155 super(clientConnection, operationID, messageID, requestControls); 156 157 this.protocolVersion = protocolVersion; 158 this.authType = AuthenticationType.SASL; 159 this.saslMechanism = saslMechanism; 160 this.saslCredentials = saslCredentials; 161 162 setRawBindDN(rawBindDN); 163 164 cancelResult = getBindCancelResult(); 165 } 166 167 /** 168 * Creates a new simple bind operation with the provided information. 169 * 170 * @param clientConnection The client connection with which this operation 171 * is associated. 172 * @param operationID The operation ID for this operation. 173 * @param messageID The message ID of the request with which this 174 * operation is associated. 175 * @param requestControls The set of controls included in the request. 176 * @param protocolVersion The string representation of the protocol version 177 * associated with this bind request. 178 * @param bindDN The bind DN for this bind operation. 179 * @param simplePassword The password to use for the simple 180 * authentication. 181 */ 182 public BindOperationBasis(ClientConnection clientConnection, long operationID, 183 int messageID, List<Control> requestControls, 184 String protocolVersion, DN bindDN, 185 ByteString simplePassword) 186 { 187 super(clientConnection, operationID, messageID, requestControls); 188 189 this.protocolVersion = protocolVersion; 190 this.bindDN = bindDN; 191 192 rawBindDN = computeRawBindDN(bindDN); 193 194 setSimplePassword(simplePassword); 195 196 cancelResult = getBindCancelResult(); 197 } 198 199 200 201 /** 202 * Creates a new SASL bind operation with the provided information. 203 * 204 * @param clientConnection The client connection with which this operation 205 * is associated. 206 * @param operationID The operation ID for this operation. 207 * @param messageID The message ID of the request with which this 208 * operation is associated. 209 * @param requestControls The set of controls included in the request. 210 * @param protocolVersion The string representation of the protocol version 211 * associated with this bind request. 212 * @param bindDN The bind DN for this bind operation. 213 * @param saslMechanism The SASL mechanism included in the request. 214 * @param saslCredentials The optional SASL credentials included in the 215 * request. 216 */ 217 public BindOperationBasis(ClientConnection clientConnection, long operationID, 218 int messageID, List<Control> requestControls, 219 String protocolVersion, DN bindDN, 220 String saslMechanism, ByteString saslCredentials) 221 { 222 super(clientConnection, operationID, messageID, requestControls); 223 224 this.protocolVersion = protocolVersion; 225 this.authType = AuthenticationType.SASL; 226 this.bindDN = bindDN; 227 this.saslMechanism = saslMechanism; 228 this.saslCredentials = saslCredentials; 229 230 rawBindDN = computeRawBindDN(bindDN); 231 232 cancelResult = getBindCancelResult(); 233 } 234 235 private ByteString computeRawBindDN(DN bindDN) 236 { 237 if (bindDN != null) 238 { 239 return ByteString.valueOfUtf8(bindDN.toString()); 240 } 241 return ByteString.empty(); 242 } 243 244 private CancelResult getBindCancelResult() 245 { 246 return new CancelResult(CANNOT_CANCEL, ERR_CANNOT_CANCEL_BIND.get()); 247 } 248 249 /** {@inheritDoc} */ 250 @Override 251 public DN getProxiedAuthorizationDN() 252 { 253 return null; 254 } 255 256 /** {@inheritDoc} */ 257 @Override 258 public void setProxiedAuthorizationDN(DN proxiedAuthorizationDN) 259 { 260 } 261 262 /** {@inheritDoc} */ 263 @Override 264 public final AuthenticationType getAuthenticationType() 265 { 266 return authType; 267 } 268 269 /** {@inheritDoc} */ 270 @Override 271 public final ByteString getRawBindDN() 272 { 273 return rawBindDN; 274 } 275 276 /** {@inheritDoc} */ 277 @Override 278 public final void setRawBindDN(ByteString rawBindDN) 279 { 280 if (rawBindDN != null) 281 { 282 this.rawBindDN = rawBindDN; 283 } 284 else 285 { 286 this.rawBindDN = ByteString.empty(); 287 } 288 289 bindDN = null; 290 } 291 292 @Override 293 public final DN getBindDN() 294 { 295 try 296 { 297 if (bindDN == null) 298 { 299 bindDN = DN.valueOf(rawBindDN); 300 } 301 } 302 catch (LocalizedIllegalArgumentException e) 303 { 304 logger.traceException(e); 305 306 setResultCode(ResultCode.INVALID_CREDENTIALS); 307 setAuthFailureReason(e.getMessageObject()); 308 } 309 return bindDN; 310 } 311 312 @Override 313 public final ByteString getSimplePassword() 314 { 315 return simplePassword; 316 } 317 318 /** {@inheritDoc} */ 319 @Override 320 public final void setSimplePassword(ByteString simplePassword) 321 { 322 if (simplePassword != null) 323 { 324 this.simplePassword = simplePassword; 325 } 326 else 327 { 328 this.simplePassword = ByteString.empty(); 329 } 330 331 authType = AuthenticationType.SIMPLE; 332 saslMechanism = null; 333 saslCredentials = null; 334 } 335 336 /** {@inheritDoc} */ 337 @Override 338 public final String getSASLMechanism() 339 { 340 return saslMechanism; 341 } 342 343 /** {@inheritDoc} */ 344 @Override 345 public final ByteString getSASLCredentials() 346 { 347 return saslCredentials; 348 } 349 350 /** {@inheritDoc} */ 351 @Override 352 public final void setSASLCredentials(String saslMechanism, 353 ByteString saslCredentials) 354 { 355 this.saslMechanism = saslMechanism; 356 this.saslCredentials = saslCredentials; 357 358 authType = AuthenticationType.SASL; 359 simplePassword = null; 360 } 361 362 /** {@inheritDoc} */ 363 @Override 364 public final ByteString getServerSASLCredentials() 365 { 366 return serverSASLCredentials; 367 } 368 369 /** {@inheritDoc} */ 370 @Override 371 public final void setServerSASLCredentials(ByteString serverSASLCredentials) 372 { 373 this.serverSASLCredentials = serverSASLCredentials; 374 } 375 376 /** {@inheritDoc} */ 377 @Override 378 public final Entry getSASLAuthUserEntry() 379 { 380 return saslAuthUserEntry; 381 } 382 383 /** {@inheritDoc} */ 384 @Override 385 public final void setSASLAuthUserEntry(Entry saslAuthUserEntry) 386 { 387 this.saslAuthUserEntry = saslAuthUserEntry; 388 } 389 390 /** {@inheritDoc} */ 391 @Override 392 public final LocalizableMessage getAuthFailureReason() 393 { 394 return authFailureReason; 395 } 396 397 /** {@inheritDoc} */ 398 @Override 399 public final void setAuthFailureReason(LocalizableMessage message) 400 { 401 if (DirectoryServer.returnBindErrorMessages()) 402 { 403 appendErrorMessage(message); 404 } 405 else 406 { 407 authFailureReason = message; 408 } 409 } 410 411 /** {@inheritDoc} */ 412 @Override 413 public final DN getUserEntryDN() 414 { 415 return userEntryDN; 416 } 417 418 /** {@inheritDoc} */ 419 @Override 420 public final AuthenticationInfo getAuthenticationInfo() 421 { 422 return authInfo; 423 } 424 425 /** {@inheritDoc} */ 426 @Override 427 public final void setAuthenticationInfo(AuthenticationInfo authInfo) 428 { 429 this.authInfo = authInfo; 430 } 431 432 /** {@inheritDoc} */ 433 @Override 434 public final OperationType getOperationType() 435 { 436 // Note that no debugging will be done in this method because it is a likely 437 // candidate for being called by the logging subsystem. 438 return OperationType.BIND; 439 } 440 441 /** {@inheritDoc} */ 442 @Override 443 public final List<Control> getResponseControls() 444 { 445 return responseControls; 446 } 447 448 /** {@inheritDoc} */ 449 @Override 450 public final void addResponseControl(Control control) 451 { 452 responseControls.add(control); 453 } 454 455 /** {@inheritDoc} */ 456 @Override 457 public final void removeResponseControl(Control control) 458 { 459 responseControls.remove(control); 460 } 461 462 /** {@inheritDoc} */ 463 @Override 464 public final void toString(StringBuilder buffer) 465 { 466 buffer.append("BindOperation(connID="); 467 buffer.append(clientConnection.getConnectionID()); 468 buffer.append(", opID="); 469 buffer.append(operationID); 470 buffer.append(", protocol=\""); 471 buffer.append(clientConnection.getProtocol()); 472 buffer.append(" "); 473 buffer.append(protocolVersion); 474 buffer.append(", dn="); 475 buffer.append(rawBindDN); 476 buffer.append(", authType="); 477 buffer.append(authType); 478 buffer.append(")"); 479 } 480 481 /** {@inheritDoc} */ 482 @Override 483 public void setUserEntryDN(DN userEntryDN) 484 { 485 this.userEntryDN = userEntryDN; 486 } 487 488 /** {@inheritDoc} */ 489 @Override 490 public String getProtocolVersion() 491 { 492 return protocolVersion; 493 } 494 495 /** {@inheritDoc} */ 496 @Override 497 public void setProtocolVersion(String protocolVersion) 498 { 499 this.protocolVersion = protocolVersion; 500 } 501 502 /** {@inheritDoc} */ 503 @Override 504 public final void run() 505 { 506 // Start the processing timer and initially set the result to indicate that 507 // the result is unknown. 508 setResultCode(ResultCode.UNDEFINED); 509 setProcessingStartTime(); 510 511 logBindRequest(this); 512 513 // Wipe out any existing authentication for the client connection and create 514 // a placeholder that will be used if the bind is successful. 515 ClientConnection clientConnection = getClientConnection(); 516 clientConnection.setUnauthenticated(); 517 518 // Abandon any operations that may be in progress for the client. 519 LocalizableMessage cancelReason = INFO_CANCELED_BY_BIND_REQUEST.get(); 520 CancelRequest cancelRequest = new CancelRequest(true, cancelReason); 521 clientConnection.cancelAllOperationsExcept(cancelRequest, getMessageID()); 522 523 524 // This flag is set to true as soon as a workflow has been executed. 525 boolean workflowExecuted = false; 526 try 527 { 528 // Invoke the pre-parse bind plugins. 529 if (!processOperationResult(getPluginConfigManager().invokePreParseBindPlugins(this))) 530 { 531 return; 532 } 533 534 535 // Process the bind DN to convert it from the raw form as provided by the 536 // client into the form required for the rest of the bind processing. 537 DN bindDN = getBindDN(); 538 if (bindDN == null){ 539 return; 540 } 541 542 // If this is a simple bind 543 // Then check whether the bind DN is actually one of the alternate root DNs 544 // defined in the server. If so, then replace it with the actual DN 545 // for that user. 546 switch (getAuthenticationType()) 547 { 548 case SIMPLE: 549 DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN); 550 if (actualRootDN != null) 551 { 552 bindDN = actualRootDN; 553 } 554 } 555 556 workflowExecuted = execute(this, bindDN); 557 } 558 catch(CanceledOperationException coe) 559 { 560 // This shouldn't happen for bind operations. Just cancel anyways 561 logger.traceException(coe); 562 563 setResultCode(ResultCode.CANCELLED); 564 565 appendErrorMessage(cancelRequest.getCancelReason()); 566 } 567 finally 568 { 569 setProcessingStopTime(); 570 logBindResponse(this); 571 572 // Send the bind response to the client. 573 clientConnection.sendResponse(this); 574 575 // If the bind processing is finished, then unset the "bind in progress" 576 // flag to allow other operations to be processed on the connection. 577 if (getResultCode() != ResultCode.SASL_BIND_IN_PROGRESS) 578 { 579 clientConnection.finishSaslBind(); 580 } 581 clientConnection.finishBind(); 582 583 invokePostResponsePlugins(workflowExecuted); 584 } 585 } 586 587 /** 588 * Invokes the post response plugins. If a workflow has been executed 589 * then invoke the post response plugins provided by the workflow 590 * elements of the workflow, otherwise invoke the post response plugins 591 * that have been registered with the current operation. 592 * 593 * @param workflowExecuted <code>true</code> if a workflow has been executed 594 */ 595 private void invokePostResponsePlugins(boolean workflowExecuted) 596 { 597 // Invoke the post response plugins 598 if (workflowExecuted) 599 { 600 // The post responses are provided by the workflow elements of the workflow. 601 List localOperations = (List) getAttachment(Operation.LOCALBACKENDOPERATIONS); 602 if (localOperations != null) 603 { 604 for (Object localOp : localOperations) 605 { 606 LocalBackendBindOperation localOperation = (LocalBackendBindOperation) localOp; 607 // Invoke the post-response bind plugins. 608 getPluginConfigManager().invokePostResponseBindPlugins(localOperation); 609 } 610 } 611 else 612 { 613 // The current operation does not implement any bind post response 614 // interface so we cannot invoke any post-response plugin. 615 } 616 } 617 } 618 619 /** {@inheritDoc} */ 620 @Override 621 public void updateOperationErrMsgAndResCode() 622 { 623 LocalizableMessage message = ERR_BIND_OPERATION_UNKNOWN_USER.get(); 624 setResultCode(ResultCode.INVALID_CREDENTIALS); 625 setAuthFailureReason(message); 626 } 627}