001/* 002 * Copyright 2007-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.util.Arrays; 026import java.util.concurrent.LinkedBlockingQueue; 027import java.util.concurrent.TimeUnit; 028 029import com.unboundid.asn1.ASN1Buffer; 030import com.unboundid.asn1.ASN1BufferSequence; 031import com.unboundid.asn1.ASN1Element; 032import com.unboundid.asn1.ASN1Integer; 033import com.unboundid.asn1.ASN1OctetString; 034import com.unboundid.asn1.ASN1Sequence; 035import com.unboundid.ldap.protocol.LDAPMessage; 036import com.unboundid.ldap.protocol.LDAPResponse; 037import com.unboundid.ldap.protocol.ProtocolOp; 038import com.unboundid.util.InternalUseOnly; 039import com.unboundid.util.LDAPSDKUsageException; 040import com.unboundid.util.NotMutable; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043 044import static com.unboundid.ldap.sdk.LDAPMessages.*; 045import static com.unboundid.util.Debug.*; 046import static com.unboundid.util.StaticUtils.*; 047 048 049 050/** 051 * This class implements the processing necessary to perform an LDAPv3 simple 052 * bind operation, which authenticates using a bind DN and password. 053 */ 054@NotMutable() 055@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 056public final class SimpleBindRequest 057 extends BindRequest 058 implements ResponseAcceptor, ProtocolOp 059{ 060 /** 061 * The BER type to use for the credentials element in a simple bind request 062 * protocol op. 063 */ 064 private static final byte CRED_TYPE_SIMPLE = (byte) 0x80; 065 066 067 068 /** 069 * The ASN.1 octet string that will be used for the bind DN if none was 070 * provided. 071 */ 072 private static final ASN1OctetString NO_BIND_DN = new ASN1OctetString(); 073 074 075 076 /** 077 * The ASN.1 octet string that will be used for the bind password if none was 078 * provided. 079 */ 080 private static final ASN1OctetString NO_PASSWORD = 081 new ASN1OctetString(CRED_TYPE_SIMPLE); 082 083 084 085 /** 086 * The serial version UID for this serializable class. 087 */ 088 private static final long serialVersionUID = 4725871243149974407L; 089 090 091 092 // The message ID from the last LDAP message sent from this request. 093 private int messageID = -1; 094 095 // The bind DN for this simple bind request. 096 private final ASN1OctetString bindDN; 097 098 // The password for this simple bind request. 099 private final ASN1OctetString password; 100 101 // The queue that will be used to receive response messages from the server. 102 private final LinkedBlockingQueue<LDAPResponse> responseQueue = 103 new LinkedBlockingQueue<LDAPResponse>(); 104 105 // The password provider that should be used to obtain the password for this 106 // simple bind request. 107 private final PasswordProvider passwordProvider; 108 109 110 111 /** 112 * Creates a new simple bind request that may be used to perform an anonymous 113 * bind to the directory server (i.e., with a zero-length bind DN and a 114 * zero-length password). 115 */ 116 public SimpleBindRequest() 117 { 118 this(NO_BIND_DN, NO_PASSWORD, null, NO_CONTROLS); 119 } 120 121 122 123 /** 124 * Creates a new simple bind request with the provided bind DN and password. 125 * 126 * @param bindDN The bind DN for this simple bind request. 127 * @param password The password for this simple bind request. 128 */ 129 public SimpleBindRequest(final String bindDN, final String password) 130 { 131 this(bindDN, password, NO_CONTROLS); 132 } 133 134 135 136 /** 137 * Creates a new simple bind request with the provided bind DN and password. 138 * 139 * @param bindDN The bind DN for this simple bind request. 140 * @param password The password for this simple bind request. 141 */ 142 public SimpleBindRequest(final String bindDN, final byte[] password) 143 { 144 this(bindDN, password, NO_CONTROLS); 145 } 146 147 148 149 /** 150 * Creates a new simple bind request with the provided bind DN and password. 151 * 152 * @param bindDN The bind DN for this simple bind request. 153 * @param password The password for this simple bind request. 154 */ 155 public SimpleBindRequest(final DN bindDN, final String password) 156 { 157 this(bindDN, password, NO_CONTROLS); 158 } 159 160 161 162 /** 163 * Creates a new simple bind request with the provided bind DN and password. 164 * 165 * @param bindDN The bind DN for this simple bind request. 166 * @param password The password for this simple bind request. 167 */ 168 public SimpleBindRequest(final DN bindDN, final byte[] password) 169 { 170 this(bindDN, password, NO_CONTROLS); 171 } 172 173 174 175 /** 176 * Creates a new simple bind request with the provided bind DN and password. 177 * 178 * @param bindDN The bind DN for this simple bind request. 179 * @param password The password for this simple bind request. 180 * @param controls The set of controls for this simple bind request. 181 */ 182 public SimpleBindRequest(final String bindDN, final String password, 183 final Control... controls) 184 { 185 super(controls); 186 187 if (bindDN == null) 188 { 189 this.bindDN = NO_BIND_DN; 190 } 191 else 192 { 193 this.bindDN = new ASN1OctetString(bindDN); 194 } 195 196 if (password == null) 197 { 198 this.password = NO_PASSWORD; 199 } 200 else 201 { 202 this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password); 203 } 204 205 passwordProvider = null; 206 } 207 208 209 210 /** 211 * Creates a new simple bind request with the provided bind DN and password. 212 * 213 * @param bindDN The bind DN for this simple bind request. 214 * @param password The password for this simple bind request. 215 * @param controls The set of controls for this simple bind request. 216 */ 217 public SimpleBindRequest(final String bindDN, final byte[] password, 218 final Control... controls) 219 { 220 super(controls); 221 222 if (bindDN == null) 223 { 224 this.bindDN = NO_BIND_DN; 225 } 226 else 227 { 228 this.bindDN = new ASN1OctetString(bindDN); 229 } 230 231 if (password == null) 232 { 233 this.password = NO_PASSWORD; 234 } 235 else 236 { 237 this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password); 238 } 239 240 passwordProvider = null; 241 } 242 243 244 245 /** 246 * Creates a new simple bind request with the provided bind DN and password. 247 * 248 * @param bindDN The bind DN for this simple bind request. 249 * @param password The password for this simple bind request. 250 * @param controls The set of controls for this simple bind request. 251 */ 252 public SimpleBindRequest(final DN bindDN, final String password, 253 final Control... controls) 254 { 255 super(controls); 256 257 if (bindDN == null) 258 { 259 this.bindDN = NO_BIND_DN; 260 } 261 else 262 { 263 this.bindDN = new ASN1OctetString(bindDN.toString()); 264 } 265 266 if (password == null) 267 { 268 this.password = NO_PASSWORD; 269 } 270 else 271 { 272 this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password); 273 } 274 275 passwordProvider = null; 276 } 277 278 279 280 /** 281 * Creates a new simple bind request with the provided bind DN and password. 282 * 283 * @param bindDN The bind DN for this simple bind request. 284 * @param password The password for this simple bind request. 285 * @param controls The set of controls for this simple bind request. 286 */ 287 public SimpleBindRequest(final DN bindDN, final byte[] password, 288 final Control... controls) 289 { 290 super(controls); 291 292 if (bindDN == null) 293 { 294 this.bindDN = NO_BIND_DN; 295 } 296 else 297 { 298 this.bindDN = new ASN1OctetString(bindDN.toString()); 299 } 300 301 if (password == null) 302 { 303 this.password = NO_PASSWORD; 304 } 305 else 306 { 307 this.password = new ASN1OctetString(CRED_TYPE_SIMPLE, password); 308 } 309 310 passwordProvider = null; 311 } 312 313 314 315 /** 316 * Creates a new simple bind request with the provided bind DN and that will 317 * use a password provider in order to obtain the bind password. 318 * 319 * @param bindDN The bind DN for this simple bind request. It 320 * must not be {@code null}. 321 * @param passwordProvider The password provider that will be used to obtain 322 * the password for this simple bind request. It 323 * must not be {@code null}. 324 * @param controls The set of controls for this simple bind request. 325 */ 326 public SimpleBindRequest(final String bindDN, 327 final PasswordProvider passwordProvider, 328 final Control... controls) 329 { 330 super(controls); 331 332 this.bindDN = new ASN1OctetString(bindDN); 333 this.passwordProvider = passwordProvider; 334 335 password = null; 336 } 337 338 339 340 /** 341 * Creates a new simple bind request with the provided bind DN and that will 342 * use a password provider in order to obtain the bind password. 343 * 344 * @param bindDN The bind DN for this simple bind request. It 345 * must not be {@code null}. 346 * @param passwordProvider The password provider that will be used to obtain 347 * the password for this simple bind request. It 348 * must not be {@code null}. 349 * @param controls The set of controls for this simple bind request. 350 */ 351 public SimpleBindRequest(final DN bindDN, 352 final PasswordProvider passwordProvider, 353 final Control... controls) 354 { 355 super(controls); 356 357 this.bindDN = new ASN1OctetString(bindDN.toString()); 358 this.passwordProvider = passwordProvider; 359 360 password = null; 361 } 362 363 364 365 /** 366 * Creates a new simple bind request with the provided bind DN and password. 367 * 368 * @param bindDN The bind DN for this simple bind request. 369 * @param password The password for this simple bind request. 370 * @param passwordProvider The password provider that will be used to obtain 371 * the password to use for the bind request. 372 * @param controls The set of controls for this simple bind request. 373 */ 374 private SimpleBindRequest(final ASN1OctetString bindDN, 375 final ASN1OctetString password, 376 final PasswordProvider passwordProvider, 377 final Control... controls) 378 { 379 super(controls); 380 381 this.bindDN = bindDN; 382 this.password = password; 383 this.passwordProvider = passwordProvider; 384 } 385 386 387 388 /** 389 * Retrieves the bind DN for this simple bind request. 390 * 391 * @return The bind DN for this simple bind request. 392 */ 393 public String getBindDN() 394 { 395 return bindDN.stringValue(); 396 } 397 398 399 400 /** 401 * Retrieves the password for this simple bind request, if no password 402 * provider has been configured. 403 * 404 * @return The password for this simple bind request, or {@code null} if a 405 * password provider will be used to obtain the password. 406 */ 407 public ASN1OctetString getPassword() 408 { 409 return password; 410 } 411 412 413 414 /** 415 * Retrieves the password provider for this simple bind request, if defined. 416 * 417 * @return The password provider for this simple bind request, or 418 * {@code null} if this bind request was created with an explicit 419 * password rather than a password provider. 420 */ 421 public PasswordProvider getPasswordProvider() 422 { 423 return passwordProvider; 424 } 425 426 427 428 /** 429 * {@inheritDoc} 430 */ 431 public byte getProtocolOpType() 432 { 433 return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST; 434 } 435 436 437 438 /** 439 * {@inheritDoc} 440 */ 441 public void writeTo(final ASN1Buffer buffer) 442 { 443 final ASN1BufferSequence requestSequence = 444 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST); 445 buffer.addElement(VERSION_ELEMENT); 446 buffer.addElement(bindDN); 447 448 if (passwordProvider == null) 449 { 450 buffer.addElement(password); 451 } 452 else 453 { 454 byte[] pwBytes; 455 try 456 { 457 pwBytes = passwordProvider.getPasswordBytes(); 458 } 459 catch (final LDAPException le) 460 { 461 debugException(le); 462 throw new LDAPRuntimeException(le); 463 } 464 465 final ASN1OctetString pw = new ASN1OctetString(CRED_TYPE_SIMPLE, pwBytes); 466 buffer.addElement(pw); 467 buffer.setZeroBufferOnClear(); 468 Arrays.fill(pwBytes, (byte) 0x00); 469 } 470 471 requestSequence.end(); 472 } 473 474 475 476 /** 477 * {@inheritDoc} 478 * Use of this method is only supported if the bind request was created with a 479 * static password. It is not allowed if the password will be obtained 480 * through a password provider. 481 * 482 * @throws LDAPSDKUsageException If this bind request was created with a 483 * password provider rather than a static 484 * password. 485 */ 486 public ASN1Element encodeProtocolOp() 487 throws LDAPSDKUsageException 488 { 489 if (password == null) 490 { 491 throw new LDAPSDKUsageException( 492 ERR_SIMPLE_BIND_ENCODE_PROTOCOL_OP_WITH_PROVIDER.get()); 493 } 494 495 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST, 496 new ASN1Integer(3), 497 bindDN, 498 password); 499 } 500 501 502 503 /** 504 * {@inheritDoc} 505 */ 506 @Override() 507 protected BindResult process(final LDAPConnection connection, final int depth) 508 throws LDAPException 509 { 510 if (connection.synchronousMode()) 511 { 512 return processSync(connection, 513 connection.getConnectionOptions().autoReconnect()); 514 } 515 516 // See if a bind DN was provided without a password. If that is the case 517 // and this should not be allowed, then throw an exception. 518 if (password != null) 519 { 520 if ((bindDN.getValue().length > 0) && (password.getValue().length == 0) && 521 connection.getConnectionOptions().bindWithDNRequiresPassword()) 522 { 523 final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR, 524 ERR_SIMPLE_BIND_DN_WITHOUT_PASSWORD.get()); 525 debugCodingError(le); 526 throw le; 527 } 528 } 529 530 531 // Create the LDAP message. 532 messageID = connection.nextMessageID(); 533 final LDAPMessage message = new LDAPMessage(messageID, this, getControls()); 534 535 536 // Register with the connection reader to be notified of responses for the 537 // request that we've created. 538 connection.registerResponseAcceptor(messageID, this); 539 540 541 try 542 { 543 // Send the request to the server. 544 debugLDAPRequest(this); 545 final long requestTime = System.nanoTime(); 546 connection.getConnectionStatistics().incrementNumBindRequests(); 547 connection.sendMessage(message); 548 549 // Wait for and process the response. 550 final LDAPResponse response; 551 try 552 { 553 final long responseTimeout = getResponseTimeoutMillis(connection); 554 if (responseTimeout > 0) 555 { 556 response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS); 557 } 558 else 559 { 560 response = responseQueue.take(); 561 } 562 } 563 catch (InterruptedException ie) 564 { 565 debugException(ie); 566 throw new LDAPException(ResultCode.LOCAL_ERROR, 567 ERR_BIND_INTERRUPTED.get(connection.getHostPort()), ie); 568 } 569 570 return handleResponse(connection, response, requestTime, false); 571 } 572 finally 573 { 574 connection.deregisterResponseAcceptor(messageID); 575 } 576 } 577 578 579 580 /** 581 * Processes this bind operation in synchronous mode, in which the same 582 * thread will send the request and read the response. 583 * 584 * @param connection The connection to use to communicate with the directory 585 * server. 586 * @param allowRetry Indicates whether the request may be re-tried on a 587 * re-established connection if the initial attempt fails 588 * in a way that indicates the connection is no longer 589 * valid and autoReconnect is true. 590 * 591 * @return An LDAP result object that provides information about the result 592 * of the bind processing. 593 * 594 * @throws LDAPException If a problem occurs while sending the request or 595 * reading the response. 596 */ 597 private BindResult processSync(final LDAPConnection connection, 598 final boolean allowRetry) 599 throws LDAPException 600 { 601 // Create the LDAP message. 602 messageID = connection.nextMessageID(); 603 final LDAPMessage message = 604 new LDAPMessage(messageID, this, getControls()); 605 606 607 // Set the appropriate timeout on the socket. 608 try 609 { 610 connection.getConnectionInternals(true).getSocket().setSoTimeout( 611 (int) getResponseTimeoutMillis(connection)); 612 } 613 catch (Exception e) 614 { 615 debugException(e); 616 } 617 618 619 // Send the request to the server. 620 final long requestTime = System.nanoTime(); 621 debugLDAPRequest(this); 622 connection.getConnectionStatistics().incrementNumBindRequests(); 623 try 624 { 625 connection.sendMessage(message); 626 } 627 catch (final LDAPException le) 628 { 629 debugException(le); 630 631 if (allowRetry) 632 { 633 final BindResult bindResult = reconnectAndRetry(connection, 634 le.getResultCode()); 635 if (bindResult != null) 636 { 637 return bindResult; 638 } 639 } 640 } 641 642 while (true) 643 { 644 final LDAPResponse response = connection.readResponse(messageID); 645 if (response instanceof IntermediateResponse) 646 { 647 final IntermediateResponseListener listener = 648 getIntermediateResponseListener(); 649 if (listener != null) 650 { 651 listener.intermediateResponseReturned( 652 (IntermediateResponse) response); 653 } 654 } 655 else 656 { 657 return handleResponse(connection, response, requestTime, allowRetry); 658 } 659 } 660 } 661 662 663 664 /** 665 * Performs the necessary processing for handling a response. 666 * 667 * @param connection The connection used to read the response. 668 * @param response The response to be processed. 669 * @param requestTime The time the request was sent to the server. 670 * @param allowRetry Indicates whether the request may be re-tried on a 671 * re-established connection if the initial attempt fails 672 * in a way that indicates the connection is no longer 673 * valid and autoReconnect is true. 674 * 675 * @return The bind result. 676 * 677 * @throws LDAPException If a problem occurs. 678 */ 679 private BindResult handleResponse(final LDAPConnection connection, 680 final LDAPResponse response, 681 final long requestTime, 682 final boolean allowRetry) 683 throws LDAPException 684 { 685 if (response == null) 686 { 687 final long waitTime = nanosToMillis(System.nanoTime() - requestTime); 688 throw new LDAPException(ResultCode.TIMEOUT, 689 ERR_SIMPLE_BIND_CLIENT_TIMEOUT.get(waitTime, messageID, 690 bindDN.stringValue(), connection.getHostPort())); 691 } 692 693 connection.getConnectionStatistics().incrementNumBindResponses( 694 System.nanoTime() - requestTime); 695 if (response instanceof ConnectionClosedResponse) 696 { 697 // The connection was closed while waiting for the response. 698 if (allowRetry) 699 { 700 final BindResult retryResult = reconnectAndRetry(connection, 701 ResultCode.SERVER_DOWN); 702 if (retryResult != null) 703 { 704 return retryResult; 705 } 706 } 707 708 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response; 709 final String message = ccr.getMessage(); 710 if (message == null) 711 { 712 throw new LDAPException(ccr.getResultCode(), 713 ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE.get( 714 connection.getHostPort(), toString())); 715 } 716 else 717 { 718 throw new LDAPException(ccr.getResultCode(), 719 ERR_CONN_CLOSED_WAITING_FOR_BIND_RESPONSE_WITH_MESSAGE.get( 720 connection.getHostPort(), toString(), message)); 721 } 722 } 723 724 final BindResult bindResult = (BindResult) response; 725 if (allowRetry) 726 { 727 final BindResult retryResult = reconnectAndRetry(connection, 728 bindResult.getResultCode()); 729 if (retryResult != null) 730 { 731 return retryResult; 732 } 733 } 734 735 return bindResult; 736 } 737 738 739 740 /** 741 * Attempts to re-establish the connection and retry processing this request 742 * on it. 743 * 744 * @param connection The connection to be re-established. 745 * @param resultCode The result code for the previous operation attempt. 746 * 747 * @return The result from re-trying the bind, or {@code null} if it could 748 * not be re-tried. 749 */ 750 private BindResult reconnectAndRetry(final LDAPConnection connection, 751 final ResultCode resultCode) 752 { 753 try 754 { 755 // We will only want to retry for certain result codes that indicate a 756 // connection problem. 757 switch (resultCode.intValue()) 758 { 759 case ResultCode.SERVER_DOWN_INT_VALUE: 760 case ResultCode.DECODING_ERROR_INT_VALUE: 761 case ResultCode.CONNECT_ERROR_INT_VALUE: 762 connection.reconnect(); 763 return processSync(connection, false); 764 } 765 } 766 catch (final Exception e) 767 { 768 debugException(e); 769 } 770 771 return null; 772 } 773 774 775 776 /** 777 * {@inheritDoc} 778 */ 779 @Override() 780 public SimpleBindRequest getRebindRequest(final String host, final int port) 781 { 782 return new SimpleBindRequest(bindDN, password, passwordProvider, 783 getControls()); 784 } 785 786 787 788 /** 789 * {@inheritDoc} 790 */ 791 @InternalUseOnly() 792 public void responseReceived(final LDAPResponse response) 793 throws LDAPException 794 { 795 try 796 { 797 responseQueue.put(response); 798 } 799 catch (Exception e) 800 { 801 debugException(e); 802 throw new LDAPException(ResultCode.LOCAL_ERROR, 803 ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e); 804 } 805 } 806 807 808 809 /** 810 * {@inheritDoc} 811 */ 812 @Override() 813 public String getBindType() 814 { 815 return "SIMPLE"; 816 } 817 818 819 820 /** 821 * {@inheritDoc} 822 */ 823 @Override() 824 public int getLastMessageID() 825 { 826 return messageID; 827 } 828 829 830 831 /** 832 * {@inheritDoc} 833 */ 834 @Override() 835 public SimpleBindRequest duplicate() 836 { 837 return duplicate(getControls()); 838 } 839 840 841 842 /** 843 * {@inheritDoc} 844 */ 845 @Override() 846 public SimpleBindRequest duplicate(final Control[] controls) 847 { 848 final SimpleBindRequest bindRequest = 849 new SimpleBindRequest(bindDN, password, passwordProvider, controls); 850 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 851 return bindRequest; 852 } 853 854 855 856 /** 857 * {@inheritDoc} 858 */ 859 @Override() 860 public void toString(final StringBuilder buffer) 861 { 862 buffer.append("SimpleBindRequest(dn='"); 863 buffer.append(bindDN); 864 buffer.append('\''); 865 866 final Control[] controls = getControls(); 867 if (controls.length > 0) 868 { 869 buffer.append(", controls={"); 870 for (int i=0; i < controls.length; i++) 871 { 872 if (i > 0) 873 { 874 buffer.append(", "); 875 } 876 877 buffer.append(controls[i]); 878 } 879 buffer.append('}'); 880 } 881 882 buffer.append(')'); 883 } 884}