001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2016 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     */
021    package com.unboundid.ldap.protocol;
022    
023    
024    
025    import com.unboundid.asn1.ASN1Buffer;
026    import com.unboundid.asn1.ASN1BufferSequence;
027    import com.unboundid.asn1.ASN1Element;
028    import com.unboundid.asn1.ASN1Integer;
029    import com.unboundid.asn1.ASN1OctetString;
030    import com.unboundid.asn1.ASN1Sequence;
031    import com.unboundid.asn1.ASN1StreamReader;
032    import com.unboundid.asn1.ASN1StreamReaderSequence;
033    import com.unboundid.ldap.sdk.BindRequest;
034    import com.unboundid.ldap.sdk.Control;
035    import com.unboundid.ldap.sdk.GenericSASLBindRequest;
036    import com.unboundid.ldap.sdk.LDAPException;
037    import com.unboundid.ldap.sdk.ResultCode;
038    import com.unboundid.ldap.sdk.SimpleBindRequest;
039    import com.unboundid.util.LDAPSDKUsageException;
040    import com.unboundid.util.NotMutable;
041    import com.unboundid.util.InternalUseOnly;
042    import com.unboundid.util.ThreadSafety;
043    import com.unboundid.util.ThreadSafetyLevel;
044    
045    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
046    import static com.unboundid.util.Debug.*;
047    import static com.unboundid.util.StaticUtils.*;
048    import 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)
058    public 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    }