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