001/*
002 * Copyright 2009-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2018 Ping Identity Corporation
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.Debug;
041import com.unboundid.util.InternalUseOnly;
042import com.unboundid.util.NotMutable;
043import com.unboundid.util.StaticUtils;
044import com.unboundid.util.ThreadSafety;
045import com.unboundid.util.ThreadSafetyLevel;
046import com.unboundid.util.Validator;
047
048import static com.unboundid.ldap.protocol.ProtocolMessages.*;
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      Validator.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          Validator.ensureNotNull(bindDN);
281          break;
282
283        case CRED_TYPE_SASL:
284          final ASN1StreamReaderSequence saslSequence = reader.beginSequence();
285          saslMechanism = reader.readString();
286          Validator.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(
301                    StaticUtils.toHex(credentialsType)));
302      }
303    }
304    catch (final LDAPException le)
305    {
306      Debug.debugException(le);
307      throw le;
308    }
309    catch (final Exception e)
310    {
311      Debug.debugException(e);
312
313      throw new LDAPException(ResultCode.DECODING_ERROR,
314           ERR_BIND_REQUEST_CANNOT_DECODE.get(
315                StaticUtils.getExceptionMessage(e)),
316           e);
317    }
318  }
319
320
321
322  /**
323   * Creates a new bind request protocol op with the provided information.
324   *
325   * @param  version          The protocol version.
326   * @param  bindDN           The bind DN.  It must not be {@code null} (but may
327   *                          be empty).
328   * @param  credentialsType  The type of credentials supplied.
329   * @param  simplePassword   The password for simple authentication, if
330   *                          appropriate.
331   * @param  saslMechanism    The name of the SASL mechanism, if appropriate.
332   * @param  saslCredentials  The SASL credentials, if appropriate.
333   */
334  private BindRequestProtocolOp(final int version, final String bindDN,
335                                final byte credentialsType,
336                                final ASN1OctetString simplePassword,
337                                final String saslMechanism,
338                                final ASN1OctetString saslCredentials)
339  {
340    this.version         = version;
341    this.bindDN          = bindDN;
342    this.credentialsType = credentialsType;
343    this.simplePassword  = simplePassword;
344    this.saslMechanism   = saslMechanism;
345    this.saslCredentials = saslCredentials;
346  }
347
348
349
350  /**
351   * Retrieves the protocol version for this bind request.
352   *
353   * @return  The protocol version for this bind request.
354   */
355  public int getVersion()
356  {
357    return version;
358  }
359
360
361
362  /**
363   * Retrieves the bind DN for this bind request.
364   *
365   * @return  The bind DN for this bind request, or an empty string if none was
366   *          provided.
367   */
368  public String getBindDN()
369  {
370    return bindDN;
371  }
372
373
374
375  /**
376   * Retrieves the credentials type for this bind request.  It will either be
377   * {@link #CRED_TYPE_SIMPLE} or {@link #CRED_TYPE_SASL}.
378   *
379   * @return  The credentials type for this bind request.
380   */
381  public byte getCredentialsType()
382  {
383    return credentialsType;
384  }
385
386
387
388  /**
389   * Retrieves the password to use for simple authentication.
390   *
391   * @return  The password to use for simple authentication, or {@code null} if
392   *          SASL authentication will be used.
393   */
394  public ASN1OctetString getSimplePassword()
395  {
396    return simplePassword;
397  }
398
399
400
401  /**
402   * Retrieves the name of the SASL mechanism for this bind request.
403   *
404   * @return  The name of the SASL mechanism for this bind request, or
405   *          {@code null} if simple authentication will be used.
406   */
407  public String getSASLMechanism()
408  {
409    return saslMechanism;
410  }
411
412
413
414  /**
415   * Retrieves the credentials to use for SASL authentication, if any.
416   *
417   * @return  The credentials to use for SASL authentication, or {@code null} if
418   *          there are no SASL credentials or if simple authentication will be
419   *          used.
420   */
421  public ASN1OctetString getSASLCredentials()
422  {
423    return saslCredentials;
424  }
425
426
427
428  /**
429   * {@inheritDoc}
430   */
431  @Override()
432  public byte getProtocolOpType()
433  {
434    return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
435  }
436
437
438
439  /**
440   * {@inheritDoc}
441   */
442  @Override()
443  public ASN1Element encodeProtocolOp()
444  {
445    final ASN1Element credentials;
446    if (credentialsType == CRED_TYPE_SIMPLE)
447    {
448      credentials = simplePassword;
449    }
450    else
451    {
452      if (saslCredentials == null)
453      {
454        credentials = new ASN1Sequence(CRED_TYPE_SASL,
455             new ASN1OctetString(saslMechanism));
456      }
457      else
458      {
459        credentials = new ASN1Sequence(CRED_TYPE_SASL,
460             new ASN1OctetString(saslMechanism),
461             saslCredentials);
462      }
463    }
464
465    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
466         new ASN1Integer(version),
467         new ASN1OctetString(bindDN),
468         credentials);
469  }
470
471
472
473  /**
474   * Decodes the provided ASN.1 element as a bind request protocol op.
475   *
476   * @param  element  The ASN.1 element to be decoded.
477   *
478   * @return  The decoded bind request protocol op.
479   *
480   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
481   *                         a bind request protocol op.
482   */
483  public static BindRequestProtocolOp decodeProtocolOp(
484                                           final ASN1Element element)
485         throws LDAPException
486  {
487    try
488    {
489      final ASN1Element[] elements =
490           ASN1Sequence.decodeAsSequence(element).elements();
491      final int version = ASN1Integer.decodeAsInteger(elements[0]).intValue();
492      final String bindDN =
493           ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
494
495      final ASN1OctetString saslCredentials;
496      final ASN1OctetString simplePassword;
497      final String saslMechanism;
498      switch (elements[2].getType())
499      {
500        case CRED_TYPE_SIMPLE:
501          simplePassword  = ASN1OctetString.decodeAsOctetString(elements[2]);
502          saslMechanism   = null;
503          saslCredentials = null;
504          break;
505
506        case CRED_TYPE_SASL:
507          final ASN1Element[] saslElements =
508               ASN1Sequence.decodeAsSequence(elements[2]).elements();
509          saslMechanism = ASN1OctetString.decodeAsOctetString(saslElements[0]).
510               stringValue();
511          if (saslElements.length == 1)
512          {
513            saslCredentials = null;
514          }
515          else
516          {
517            saslCredentials =
518                 ASN1OctetString.decodeAsOctetString(saslElements[1]);
519          }
520
521          simplePassword = null;
522          break;
523
524        default:
525          throw new LDAPException(ResultCode.DECODING_ERROR,
526               ERR_BIND_REQUEST_INVALID_CRED_TYPE.get(
527                    StaticUtils.toHex(elements[2].getType())));
528      }
529
530      return new BindRequestProtocolOp(version, bindDN, elements[2].getType(),
531           simplePassword, saslMechanism, saslCredentials);
532    }
533    catch (final LDAPException le)
534    {
535      Debug.debugException(le);
536      throw le;
537    }
538    catch (final Exception e)
539    {
540      Debug.debugException(e);
541      throw new LDAPException(ResultCode.DECODING_ERROR,
542           ERR_BIND_REQUEST_CANNOT_DECODE.get(
543                StaticUtils.getExceptionMessage(e)),
544           e);
545    }
546  }
547
548
549
550  /**
551   * {@inheritDoc}
552   */
553  @Override()
554  public void writeTo(final ASN1Buffer buffer)
555  {
556    final ASN1BufferSequence opSequence =
557         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
558    buffer.addInteger(version);
559    buffer.addOctetString(bindDN);
560
561    if (credentialsType == CRED_TYPE_SIMPLE)
562    {
563      buffer.addElement(simplePassword);
564    }
565    else
566    {
567      final ASN1BufferSequence saslSequence =
568           buffer.beginSequence(CRED_TYPE_SASL);
569      buffer.addOctetString(saslMechanism);
570      if (saslCredentials != null)
571      {
572        buffer.addElement(saslCredentials);
573      }
574      saslSequence.end();
575    }
576    opSequence.end();
577    buffer.setZeroBufferOnClear();
578  }
579
580
581
582  /**
583   * Creates a new bind request object from this bind request protocol op.
584   *
585   * @param  controls  The set of controls to include in the bind request.  It
586   *                   may be empty or {@code null} if no controls should be
587   *                   included.
588   *
589   * @return  The bind request that was created.
590   */
591  public BindRequest toBindRequest(final Control... controls)
592  {
593    if (credentialsType == CRED_TYPE_SIMPLE)
594    {
595      return new SimpleBindRequest(bindDN, simplePassword.getValue(),
596           controls);
597    }
598    else
599    {
600      return new GenericSASLBindRequest(bindDN, saslMechanism,
601           saslCredentials, controls);
602    }
603  }
604
605
606
607  /**
608   * Retrieves a string representation of this protocol op.
609   *
610   * @return  A string representation of this protocol op.
611   */
612  @Override()
613  public String toString()
614  {
615    final StringBuilder buffer = new StringBuilder();
616    toString(buffer);
617    return buffer.toString();
618  }
619
620
621
622  /**
623   * {@inheritDoc}
624   */
625  @Override()
626  public void toString(final StringBuilder buffer)
627  {
628    buffer.append("BindRequestProtocolOp(version=");
629    buffer.append(version);
630    buffer.append(", bindDN='");
631    buffer.append(bindDN);
632    buffer.append("', type=");
633
634    if (credentialsType == CRED_TYPE_SIMPLE)
635    {
636      buffer.append("simple");
637    }
638    else
639    {
640      buffer.append("SASL, mechanism=");
641      buffer.append(saslMechanism);
642    }
643
644    buffer.append(')');
645  }
646}