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