001/*
002 * Copyright 2009-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2017 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.protocol;
022
023
024
025import com.unboundid.asn1.ASN1Buffer;
026import com.unboundid.asn1.ASN1BufferSequence;
027import com.unboundid.asn1.ASN1Element;
028import com.unboundid.asn1.ASN1Integer;
029import com.unboundid.asn1.ASN1OctetString;
030import com.unboundid.asn1.ASN1Sequence;
031import com.unboundid.asn1.ASN1StreamReader;
032import com.unboundid.asn1.ASN1StreamReaderSequence;
033import com.unboundid.ldap.sdk.BindRequest;
034import com.unboundid.ldap.sdk.Control;
035import com.unboundid.ldap.sdk.GenericSASLBindRequest;
036import com.unboundid.ldap.sdk.LDAPException;
037import com.unboundid.ldap.sdk.ResultCode;
038import com.unboundid.ldap.sdk.SimpleBindRequest;
039import com.unboundid.util.LDAPSDKUsageException;
040import com.unboundid.util.NotMutable;
041import com.unboundid.util.InternalUseOnly;
042import com.unboundid.util.ThreadSafety;
043import com.unboundid.util.ThreadSafetyLevel;
044
045import static com.unboundid.ldap.protocol.ProtocolMessages.*;
046import static com.unboundid.util.Debug.*;
047import static com.unboundid.util.StaticUtils.*;
048import static com.unboundid.util.Validator.*;
049
050
051
052/**
053 * This class provides an implementation of an LDAP bind request protocol op.
054 */
055@InternalUseOnly()
056@NotMutable()
057@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
058public final class BindRequestProtocolOp
059       implements ProtocolOp
060{
061  /**
062   * The credentials type for simple bind requests.
063   */
064  public static final byte CRED_TYPE_SIMPLE = (byte) 0x80;
065
066
067
068  /**
069   * The credentials type for SASL bind requests.
070   */
071  public static final byte CRED_TYPE_SASL = (byte) 0xA3;
072
073
074
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = 6661208657485444954L;
079
080
081
082  // The credentials to use for SASL authentication.
083  private final ASN1OctetString saslCredentials;
084
085  // The password to use for simple authentication.
086  private final ASN1OctetString simplePassword;
087
088  // The credentials type for this bind request.
089  private final byte credentialsType;
090
091  // The protocol version for this bind request.
092  private final int version;
093
094  // The bind DN to use for this bind request.
095  private final String bindDN;
096
097  // The name of the SASL mechanism.
098  private final String saslMechanism;
099
100
101
102  /**
103   * Creates a new bind request protocol op for a simple bind.
104   *
105   * @param  bindDN    The DN for this bind request.
106   * @param  password  The password for this bind request.
107   */
108  public BindRequestProtocolOp(final String bindDN, final String password)
109  {
110    if (bindDN == null)
111    {
112      this.bindDN = "";
113    }
114    else
115    {
116      this.bindDN = bindDN;
117    }
118
119    if (password == null)
120    {
121      simplePassword = new ASN1OctetString(CRED_TYPE_SIMPLE);
122    }
123    else
124    {
125      simplePassword = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
126    }
127
128    version         = 3;
129    credentialsType = CRED_TYPE_SIMPLE;
130    saslMechanism   = null;
131    saslCredentials = null;
132  }
133
134
135
136  /**
137   * Creates a new bind request protocol op for a simple bind.
138   *
139   * @param  bindDN    The DN for this bind request.
140   * @param  password  The password for this bind request.
141   */
142  public BindRequestProtocolOp(final String bindDN, final byte[] password)
143  {
144    if (bindDN == null)
145    {
146      this.bindDN = "";
147    }
148    else
149    {
150      this.bindDN = bindDN;
151    }
152
153    if (password == null)
154    {
155      simplePassword = new ASN1OctetString(CRED_TYPE_SIMPLE);
156    }
157    else
158    {
159      simplePassword = new ASN1OctetString(CRED_TYPE_SIMPLE, password);
160    }
161
162    version         = 3;
163    credentialsType = CRED_TYPE_SIMPLE;
164    saslMechanism   = null;
165    saslCredentials = null;
166  }
167
168
169
170  /**
171   * Creates a new bind request protocol op for a SASL bind.
172   *
173   * @param  bindDN           The DN for this bind request.
174   * @param  saslMechanism    The name of the SASL mechanism for this bind
175   *                          request.  It must not be {@code null}.
176   * @param  saslCredentials  The SASL credentials for this bind request, if
177   *                          any.
178   */
179  public BindRequestProtocolOp(final String bindDN, final String saslMechanism,
180                               final ASN1OctetString saslCredentials)
181  {
182    this.saslMechanism   = saslMechanism;
183    this.saslCredentials = saslCredentials;
184
185    if (bindDN == null)
186    {
187      this.bindDN = "";
188    }
189    else
190    {
191      this.bindDN = bindDN;
192    }
193
194    version         = 3;
195    credentialsType = CRED_TYPE_SASL;
196    simplePassword  = null;
197  }
198
199
200
201  /**
202   * Creates a new bind request protocol op from the provided bind request
203   * object.
204   *
205   * @param  request  The simple bind request to use to create this protocol op.
206   *                  It must have been created with a static password rather
207   *                  than using a password provider.
208   *
209   * @throws  LDAPSDKUsageException  If the provided simple bind request is
210   *                                 configured to use a password provider
211   *                                 rather than a static password.
212   */
213  public BindRequestProtocolOp(final SimpleBindRequest request)
214         throws LDAPSDKUsageException
215  {
216    version         = 3;
217    credentialsType = CRED_TYPE_SIMPLE;
218    bindDN          = request.getBindDN();
219    simplePassword  = request.getPassword();
220    saslMechanism   = null;
221    saslCredentials = null;
222
223    if (simplePassword == null)
224    {
225      throw new LDAPSDKUsageException(
226           ERR_BIND_REQUEST_CANNOT_CREATE_WITH_PASSWORD_PROVIDER.get());
227    }
228  }
229
230
231
232  /**
233   * Creates a new bind request protocol op from the provided bind request
234   * object.
235   *
236   * @param  request  The generic SASL bind request to use to create this
237   *                  protocol op.
238   */
239  public BindRequestProtocolOp(final GenericSASLBindRequest request)
240  {
241    version         = 3;
242    credentialsType = CRED_TYPE_SASL;
243    bindDN          = request.getBindDN();
244    simplePassword  = null;
245    saslMechanism   = request.getSASLMechanismName();
246    saslCredentials = request.getCredentials();
247  }
248
249
250
251  /**
252   * Creates a new bind request protocol op read from the provided ASN.1 stream
253   * reader.
254   *
255   * @param  reader  The ASN.1 stream reader from which to read the bind request
256   *                 protocol op.
257   *
258   * @throws  LDAPException  If a problem occurs while reading or parsing the
259   *                         bind request.
260   */
261  BindRequestProtocolOp(final ASN1StreamReader reader)
262       throws LDAPException
263  {
264    try
265    {
266      reader.beginSequence();
267      version         = reader.readInteger();
268      bindDN          = reader.readString();
269      credentialsType = (byte) reader.peek();
270
271      ensureNotNull(bindDN);
272
273      switch (credentialsType)
274      {
275        case CRED_TYPE_SIMPLE:
276          simplePassword =
277               new ASN1OctetString(credentialsType, reader.readBytes());
278          saslMechanism   = null;
279          saslCredentials = null;
280          ensureNotNull(bindDN);
281          break;
282
283        case CRED_TYPE_SASL:
284          final ASN1StreamReaderSequence saslSequence = reader.beginSequence();
285          saslMechanism = reader.readString();
286          ensureNotNull(saslMechanism);
287          if (saslSequence.hasMoreElements())
288          {
289            saslCredentials = new ASN1OctetString(reader.readBytes());
290          }
291          else
292          {
293            saslCredentials = null;
294          }
295          simplePassword = null;
296          break;
297
298        default:
299          throw new LDAPException(ResultCode.DECODING_ERROR,
300               ERR_BIND_REQUEST_INVALID_CRED_TYPE.get(toHex(credentialsType)));
301      }
302    }
303    catch (LDAPException le)
304    {
305      debugException(le);
306      throw le;
307    }
308    catch (Exception e)
309    {
310      debugException(e);
311
312      throw new LDAPException(ResultCode.DECODING_ERROR,
313           ERR_BIND_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)), e);
314    }
315  }
316
317
318
319  /**
320   * Creates a new bind request protocol op with the provided information.
321   *
322   * @param  version          The protocol version.
323   * @param  bindDN           The bind DN.  It must not be {@code null} (but may
324   *                          be empty).
325   * @param  credentialsType  The type of credentials supplied.
326   * @param  simplePassword   The password for simple authentication, if
327   *                          appropriate.
328   * @param  saslMechanism    The name of the SASL mechanism, if appropriate.
329   * @param  saslCredentials  The SASL credentials, if appropriate.
330   */
331  private BindRequestProtocolOp(final int version, final String bindDN,
332                                final byte credentialsType,
333                                final ASN1OctetString simplePassword,
334                                final String saslMechanism,
335                                final ASN1OctetString saslCredentials)
336  {
337    this.version         = version;
338    this.bindDN          = bindDN;
339    this.credentialsType = credentialsType;
340    this.simplePassword  = simplePassword;
341    this.saslMechanism   = saslMechanism;
342    this.saslCredentials = saslCredentials;
343  }
344
345
346
347  /**
348   * Retrieves the protocol version for this bind request.
349   *
350   * @return  The protocol version for this bind request.
351   */
352  public int getVersion()
353  {
354    return version;
355  }
356
357
358
359  /**
360   * Retrieves the bind DN for this bind request.
361   *
362   * @return  The bind DN for this bind request, or an empty string if none was
363   *          provided.
364   */
365  public String getBindDN()
366  {
367    return bindDN;
368  }
369
370
371
372  /**
373   * Retrieves the credentials type for this bind request.  It will either be
374   * {@link #CRED_TYPE_SIMPLE} or {@link #CRED_TYPE_SASL}.
375   *
376   * @return  The credentials type for this bind request.
377   */
378  public byte getCredentialsType()
379  {
380    return credentialsType;
381  }
382
383
384
385  /**
386   * Retrieves the password to use for simple authentication.
387   *
388   * @return  The password to use for simple authentication, or {@code null} if
389   *          SASL authentication will be used.
390   */
391  public ASN1OctetString getSimplePassword()
392  {
393    return simplePassword;
394  }
395
396
397
398  /**
399   * Retrieves the name of the SASL mechanism for this bind request.
400   *
401   * @return  The name of the SASL mechanism for this bind request, or
402   *          {@code null} if simple authentication will be used.
403   */
404  public String getSASLMechanism()
405  {
406    return saslMechanism;
407  }
408
409
410
411  /**
412   * Retrieves the credentials to use for SASL authentication, if any.
413   *
414   * @return  The credentials to use for SASL authentication, or {@code null} if
415   *          there are no SASL credentials or if simple authentication will be
416   *          used.
417   */
418  public ASN1OctetString getSASLCredentials()
419  {
420    return saslCredentials;
421  }
422
423
424
425  /**
426   * {@inheritDoc}
427   */
428  public byte getProtocolOpType()
429  {
430    return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
431  }
432
433
434
435  /**
436   * {@inheritDoc}
437   */
438  public ASN1Element encodeProtocolOp()
439  {
440    final ASN1Element credentials;
441    if (credentialsType == CRED_TYPE_SIMPLE)
442    {
443      credentials = simplePassword;
444    }
445    else
446    {
447      if (saslCredentials == null)
448      {
449        credentials = new ASN1Sequence(CRED_TYPE_SASL,
450             new ASN1OctetString(saslMechanism));
451      }
452      else
453      {
454        credentials = new ASN1Sequence(CRED_TYPE_SASL,
455             new ASN1OctetString(saslMechanism),
456             saslCredentials);
457      }
458    }
459
460    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
461         new ASN1Integer(version),
462         new ASN1OctetString(bindDN),
463         credentials);
464  }
465
466
467
468  /**
469   * Decodes the provided ASN.1 element as a bind request protocol op.
470   *
471   * @param  element  The ASN.1 element to be decoded.
472   *
473   * @return  The decoded bind request protocol op.
474   *
475   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
476   *                         a bind request protocol op.
477   */
478  public static BindRequestProtocolOp decodeProtocolOp(
479                                           final ASN1Element element)
480         throws LDAPException
481  {
482    try
483    {
484      final ASN1Element[] elements =
485           ASN1Sequence.decodeAsSequence(element).elements();
486      final int version = ASN1Integer.decodeAsInteger(elements[0]).intValue();
487      final String bindDN =
488           ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
489
490      final ASN1OctetString saslCredentials;
491      final ASN1OctetString simplePassword;
492      final String saslMechanism;
493      switch (elements[2].getType())
494      {
495        case CRED_TYPE_SIMPLE:
496          simplePassword  = ASN1OctetString.decodeAsOctetString(elements[2]);
497          saslMechanism   = null;
498          saslCredentials = null;
499          break;
500
501        case CRED_TYPE_SASL:
502          final ASN1Element[] saslElements =
503               ASN1Sequence.decodeAsSequence(elements[2]).elements();
504          saslMechanism = ASN1OctetString.decodeAsOctetString(saslElements[0]).
505               stringValue();
506          if (saslElements.length == 1)
507          {
508            saslCredentials = null;
509          }
510          else
511          {
512            saslCredentials =
513                 ASN1OctetString.decodeAsOctetString(saslElements[1]);
514          }
515
516          simplePassword = null;
517          break;
518
519        default:
520          throw new LDAPException(ResultCode.DECODING_ERROR,
521               ERR_BIND_REQUEST_INVALID_CRED_TYPE.get(
522                    toHex(elements[2].getType())));
523      }
524
525      return new BindRequestProtocolOp(version, bindDN, elements[2].getType(),
526           simplePassword, saslMechanism, saslCredentials);
527    }
528    catch (final LDAPException le)
529    {
530      debugException(le);
531      throw le;
532    }
533    catch (final Exception e)
534    {
535      debugException(e);
536      throw new LDAPException(ResultCode.DECODING_ERROR,
537           ERR_BIND_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)),
538           e);
539    }
540  }
541
542
543
544  /**
545   * {@inheritDoc}
546   */
547  public void writeTo(final ASN1Buffer buffer)
548  {
549    final ASN1BufferSequence opSequence =
550         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
551    buffer.addInteger(version);
552    buffer.addOctetString(bindDN);
553
554    if (credentialsType == CRED_TYPE_SIMPLE)
555    {
556      buffer.addElement(simplePassword);
557    }
558    else
559    {
560      final ASN1BufferSequence saslSequence =
561           buffer.beginSequence(CRED_TYPE_SASL);
562      buffer.addOctetString(saslMechanism);
563      if (saslCredentials != null)
564      {
565        buffer.addElement(saslCredentials);
566      }
567      saslSequence.end();
568    }
569    opSequence.end();
570    buffer.setZeroBufferOnClear();
571  }
572
573
574
575  /**
576   * Creates a new bind request object from this bind request protocol op.
577   *
578   * @param  controls  The set of controls to include in the bind request.  It
579   *                   may be empty or {@code null} if no controls should be
580   *                   included.
581   *
582   * @return  The bind request that was created.
583   */
584  public BindRequest toBindRequest(final Control... controls)
585  {
586    if (credentialsType == CRED_TYPE_SIMPLE)
587    {
588      return new SimpleBindRequest(bindDN, simplePassword.getValue(),
589           controls);
590    }
591    else
592    {
593      return new GenericSASLBindRequest(bindDN, saslMechanism,
594           saslCredentials, controls);
595    }
596  }
597
598
599
600  /**
601   * Retrieves a string representation of this protocol op.
602   *
603   * @return  A string representation of this protocol op.
604   */
605  @Override()
606  public String toString()
607  {
608    final StringBuilder buffer = new StringBuilder();
609    toString(buffer);
610    return buffer.toString();
611  }
612
613
614
615  /**
616   * {@inheritDoc}
617   */
618  public void toString(final StringBuilder buffer)
619  {
620    buffer.append("BindRequestProtocolOp(version=");
621    buffer.append(version);
622    buffer.append(", bindDN='");
623    buffer.append(bindDN);
624    buffer.append("', type=");
625
626    if (credentialsType == CRED_TYPE_SIMPLE)
627    {
628      buffer.append("simple");
629    }
630    else
631    {
632      buffer.append("SASL, mechanism=");
633      buffer.append(saslMechanism);
634    }
635
636    buffer.append(')');
637  }
638}