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