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.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 (final LDAPException le)
304    {
305      debugException(le);
306      throw le;
307    }
308    catch (final 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  @Override()
429  public byte getProtocolOpType()
430  {
431    return LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST;
432  }
433
434
435
436  /**
437   * {@inheritDoc}
438   */
439  @Override()
440  public ASN1Element encodeProtocolOp()
441  {
442    final ASN1Element credentials;
443    if (credentialsType == CRED_TYPE_SIMPLE)
444    {
445      credentials = simplePassword;
446    }
447    else
448    {
449      if (saslCredentials == null)
450      {
451        credentials = new ASN1Sequence(CRED_TYPE_SASL,
452             new ASN1OctetString(saslMechanism));
453      }
454      else
455      {
456        credentials = new ASN1Sequence(CRED_TYPE_SASL,
457             new ASN1OctetString(saslMechanism),
458             saslCredentials);
459      }
460    }
461
462    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST,
463         new ASN1Integer(version),
464         new ASN1OctetString(bindDN),
465         credentials);
466  }
467
468
469
470  /**
471   * Decodes the provided ASN.1 element as a bind request protocol op.
472   *
473   * @param  element  The ASN.1 element to be decoded.
474   *
475   * @return  The decoded bind request protocol op.
476   *
477   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
478   *                         a bind request protocol op.
479   */
480  public static BindRequestProtocolOp decodeProtocolOp(
481                                           final ASN1Element element)
482         throws LDAPException
483  {
484    try
485    {
486      final ASN1Element[] elements =
487           ASN1Sequence.decodeAsSequence(element).elements();
488      final int version = ASN1Integer.decodeAsInteger(elements[0]).intValue();
489      final String bindDN =
490           ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
491
492      final ASN1OctetString saslCredentials;
493      final ASN1OctetString simplePassword;
494      final String saslMechanism;
495      switch (elements[2].getType())
496      {
497        case CRED_TYPE_SIMPLE:
498          simplePassword  = ASN1OctetString.decodeAsOctetString(elements[2]);
499          saslMechanism   = null;
500          saslCredentials = null;
501          break;
502
503        case CRED_TYPE_SASL:
504          final ASN1Element[] saslElements =
505               ASN1Sequence.decodeAsSequence(elements[2]).elements();
506          saslMechanism = ASN1OctetString.decodeAsOctetString(saslElements[0]).
507               stringValue();
508          if (saslElements.length == 1)
509          {
510            saslCredentials = null;
511          }
512          else
513          {
514            saslCredentials =
515                 ASN1OctetString.decodeAsOctetString(saslElements[1]);
516          }
517
518          simplePassword = null;
519          break;
520
521        default:
522          throw new LDAPException(ResultCode.DECODING_ERROR,
523               ERR_BIND_REQUEST_INVALID_CRED_TYPE.get(
524                    toHex(elements[2].getType())));
525      }
526
527      return new BindRequestProtocolOp(version, bindDN, elements[2].getType(),
528           simplePassword, saslMechanism, saslCredentials);
529    }
530    catch (final LDAPException le)
531    {
532      debugException(le);
533      throw le;
534    }
535    catch (final Exception e)
536    {
537      debugException(e);
538      throw new LDAPException(ResultCode.DECODING_ERROR,
539           ERR_BIND_REQUEST_CANNOT_DECODE.get(getExceptionMessage(e)),
540           e);
541    }
542  }
543
544
545
546  /**
547   * {@inheritDoc}
548   */
549  @Override()
550  public void writeTo(final ASN1Buffer buffer)
551  {
552    final ASN1BufferSequence opSequence =
553         buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_REQUEST);
554    buffer.addInteger(version);
555    buffer.addOctetString(bindDN);
556
557    if (credentialsType == CRED_TYPE_SIMPLE)
558    {
559      buffer.addElement(simplePassword);
560    }
561    else
562    {
563      final ASN1BufferSequence saslSequence =
564           buffer.beginSequence(CRED_TYPE_SASL);
565      buffer.addOctetString(saslMechanism);
566      if (saslCredentials != null)
567      {
568        buffer.addElement(saslCredentials);
569      }
570      saslSequence.end();
571    }
572    opSequence.end();
573    buffer.setZeroBufferOnClear();
574  }
575
576
577
578  /**
579   * Creates a new bind request object from this bind request protocol op.
580   *
581   * @param  controls  The set of controls to include in the bind request.  It
582   *                   may be empty or {@code null} if no controls should be
583   *                   included.
584   *
585   * @return  The bind request that was created.
586   */
587  public BindRequest toBindRequest(final Control... controls)
588  {
589    if (credentialsType == CRED_TYPE_SIMPLE)
590    {
591      return new SimpleBindRequest(bindDN, simplePassword.getValue(),
592           controls);
593    }
594    else
595    {
596      return new GenericSASLBindRequest(bindDN, saslMechanism,
597           saslCredentials, controls);
598    }
599  }
600
601
602
603  /**
604   * Retrieves a string representation of this protocol op.
605   *
606   * @return  A string representation of this protocol op.
607   */
608  @Override()
609  public String toString()
610  {
611    final StringBuilder buffer = new StringBuilder();
612    toString(buffer);
613    return buffer.toString();
614  }
615
616
617
618  /**
619   * {@inheritDoc}
620   */
621  @Override()
622  public void toString(final StringBuilder buffer)
623  {
624    buffer.append("BindRequestProtocolOp(version=");
625    buffer.append(version);
626    buffer.append(", bindDN='");
627    buffer.append(bindDN);
628    buffer.append("', type=");
629
630    if (credentialsType == CRED_TYPE_SIMPLE)
631    {
632      buffer.append("simple");
633    }
634    else
635    {
636      buffer.append("SASL, mechanism=");
637      buffer.append(saslMechanism);
638    }
639
640    buffer.append(')');
641  }
642}