001 /*
002 * Copyright 2012-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 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.sdk.unboundidds;
022
023
024
025 import java.util.ArrayList;
026
027 import com.unboundid.asn1.ASN1Element;
028 import com.unboundid.asn1.ASN1OctetString;
029 import com.unboundid.asn1.ASN1Sequence;
030 import com.unboundid.ldap.sdk.BindResult;
031 import com.unboundid.ldap.sdk.Control;
032 import com.unboundid.ldap.sdk.InternalSDKHelper;
033 import com.unboundid.ldap.sdk.LDAPConnection;
034 import com.unboundid.ldap.sdk.LDAPException;
035 import com.unboundid.ldap.sdk.SASLBindRequest;
036 import com.unboundid.util.NotExtensible;
037 import com.unboundid.util.ThreadSafety;
038 import com.unboundid.util.ThreadSafetyLevel;
039 import com.unboundid.util.Validator;
040
041
042
043 /**
044 * <BLOCKQUOTE>
045 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID
046 * LDAP SDK for Java. It is not available for use in applications that
047 * include only the Standard Edition of the LDAP SDK, and is not supported for
048 * use in conjunction with non-UnboundID products.
049 * </BLOCKQUOTE>
050 * This class provides support for an UnboundID-proprietary SASL mechanism that
051 * uses the time-based one-time password mechanism (TOTP) as described in
052 * <A HREF="http://www.ietf.org/rfc/rfc6238.txt">RFC 6238</A>, optionally (based
053 * on the server configuration) in conjunction with a static password for a form
054 * of multifactor authentication.
055 * <BR><BR>
056 * The name for this SASL mechanism is "UNBOUNDID-TOTP". An UNBOUNDID-TOTP SASL
057 * bind request MUST include SASL credentials with the following ASN.1 encoding:
058 * <BR><BR>
059 * <PRE>
060 * UnboundIDTOTPCredentials ::= SEQUENCE {
061 * authenticationID [0] OCTET STRING,
062 * authorizationID [1] OCTET STRING OPTIONAL,
063 * totpPassword [2] OCTET STRING,
064 * staticPassword [3] OCTET STRING OPTIONAL }
065 * </PRE>
066 * <BR><BR>
067 * Note that this class is abstract, with two different concrete
068 * implementations: the {@link SingleUseTOTPBindRequest} class may be used for
069 * cases in which the one-time password will be obtained from an external source
070 * (e.g., provided by the user, perhaps using the Google Authenticator
071 * application), and the {@link ReusableTOTPBindRequest} class may be used for
072 * cases in which the one-time password should be generated by the LDAP SDK
073 * itself. Because the {@code SingleUseTOTPBindRequest} class contains a
074 * point-in-time password, it cannot be used for re-authentication (e.g., for
075 * use with a connection pool, following referrals, or with the auto-reconnect
076 * feature). If TOTP authentication should be used in contexts where one or
077 * more of these may be needed, then the dynamic variant should be used.
078 */
079 @NotExtensible()
080 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
081 public abstract class UnboundIDTOTPBindRequest
082 extends SASLBindRequest
083 {
084 /**
085 * The name for the UnboundID TOTP SASL mechanism.
086 */
087 public static final String UNBOUNDID_TOTP_MECHANISM_NAME = "UNBOUNDID-TOTP";
088
089
090
091 /**
092 * The BER type for the authentication ID included in the request.
093 */
094 static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80;
095
096
097
098 /**
099 * The BER type for the authorization ID included in the request.
100 */
101 static final byte TYPE_AUTHORIZATION_ID = (byte) 0x81;
102
103
104
105 /**
106 * The BER type for the TOTP password included in the request.
107 */
108 static final byte TYPE_TOTP_PASSWORD = (byte) 0x82;
109
110
111
112 /**
113 * The BER type for the static password included in the request.
114 */
115 static final byte TYPE_STATIC_PASSWORD = (byte) 0x83;
116
117
118
119 /**
120 * The serial version UID for this serializable class.
121 */
122 private static final long serialVersionUID = -8751931123826994145L;
123
124
125
126 // The static password for the target user, if provided.
127 private final ASN1OctetString staticPassword;
128
129 // The message ID from the last LDAP message sent from this request.
130 private volatile int messageID = -1;
131
132 // The authentication identity for the bind.
133 private final String authenticationID;
134
135 // The authorization identity for the bind, if provided.
136 private final String authorizationID;
137
138
139
140 /**
141 * Creates a new TOTP bind request with the provided information.
142 *
143 * @param authenticationID The authentication identity for the bind request.
144 * It must not be {@code null}, and must be in the
145 * form "u:" followed by a username, or "dn:"
146 * followed by a DN.
147 * @param authorizationID The authorization identity for the bind request.
148 * It may be {@code null} if the authorization
149 * identity should be the same as the authentication
150 * identity. If an authorization identity is
151 * specified, it must be in the form "u:" followed
152 * by a username, or "dn:" followed by a DN. The
153 * value "dn:" may indicate an authorization
154 * identity of the anonymous user.
155 * @param staticPassword The static password for the target user. It may
156 * be {@code null} if only the one-time password is
157 * to be used for authentication (which may or may
158 * not be allowed by the server).
159 * @param controls The set of controls to include in the bind
160 * request.
161 */
162 protected UnboundIDTOTPBindRequest(final String authenticationID,
163 final String authorizationID,
164 final String staticPassword,
165 final Control... controls)
166 {
167 super(controls);
168
169 Validator.ensureNotNull(authenticationID);
170
171 this.authenticationID = authenticationID;
172 this.authorizationID = authorizationID;
173
174 if (staticPassword == null)
175 {
176 this.staticPassword = null;
177 }
178 else
179 {
180 this.staticPassword =
181 new ASN1OctetString(TYPE_STATIC_PASSWORD, staticPassword);
182 }
183 }
184
185
186
187 /**
188 * Creates a new TOTP bind request with the provided information.
189 *
190 * @param authenticationID The authentication identity for the bind request.
191 * It must not be {@code null}, and must be in the
192 * form "u:" followed by a username, or "dn:"
193 * followed by a DN.
194 * @param authorizationID The authorization identity for the bind request.
195 * It may be {@code null} if the authorization
196 * identity should be the same as the authentication
197 * identity. If an authorization identity is
198 * specified, it must be in the form "u:" followed
199 * by a username, or "dn:" followed by a DN. The
200 * value "dn:" may indicate an authorization
201 * identity of the anonymous user.
202 * @param staticPassword The static password for the target user. It may
203 * be {@code null} if only the one-time password is
204 * to be used for authentication (which may or may
205 * not be allowed by the server).
206 * @param controls The set of controls to include in the bind
207 * request.
208 */
209 protected UnboundIDTOTPBindRequest(final String authenticationID,
210 final String authorizationID,
211 final byte[] staticPassword,
212 final Control... controls)
213 {
214 super(controls);
215
216 Validator.ensureNotNull(authenticationID);
217
218 this.authenticationID = authenticationID;
219 this.authorizationID = authorizationID;
220
221 if (staticPassword == null)
222 {
223 this.staticPassword = null;
224 }
225 else
226 {
227 this.staticPassword =
228 new ASN1OctetString(TYPE_STATIC_PASSWORD, staticPassword);
229 }
230 }
231
232
233
234 /**
235 * Creates a new TOTP bind request with the provided information.
236 *
237 * @param authenticationID The authentication identity for the bind request.
238 * It must not be {@code null}, and must be in the
239 * form "u:" followed by a username, or "dn:"
240 * followed by a DN.
241 * @param authorizationID The authorization identity for the bind request.
242 * It may be {@code null} if the authorization
243 * identity should be the same as the authentication
244 * identity. If an authorization identity is
245 * specified, it must be in the form "u:" followed
246 * by a username, or "dn:" followed by a DN. The
247 * value "dn:" may indicate an authorization
248 * identity of the anonymous user.
249 * @param staticPassword The static password for the target user. It may
250 * be {@code null} if only the one-time password is
251 * to be used for authentication (which may or may
252 * not be allowed by the server). If it is
253 * non-{@code null}, then it must have the
254 * appropriate BER type.
255 * @param controls The set of controls to include in the bind
256 * request.
257 */
258 protected UnboundIDTOTPBindRequest(final String authenticationID,
259 final String authorizationID,
260 final ASN1OctetString staticPassword,
261 final Control... controls)
262 {
263 super(controls);
264
265 Validator.ensureNotNull(authenticationID);
266
267 if (staticPassword != null)
268 {
269 Validator.ensureTrue(staticPassword.getType() == TYPE_STATIC_PASSWORD);
270 }
271
272 this.authenticationID = authenticationID;
273 this.authorizationID = authorizationID;
274 this.staticPassword = staticPassword;
275 }
276
277
278
279 /**
280 * Retrieves the authentication ID for the bind request.
281 *
282 * @return The authentication ID for the bind request.
283 */
284 public final String getAuthenticationID()
285 {
286 return authenticationID;
287 }
288
289
290
291 /**
292 * Retrieves the authorization ID for the bind request, if one was provided.
293 *
294 * @return The authorization ID for the bind request, or {@code null} if the
295 * authorization ID should be the same as the authentication ID.
296 */
297 public final String getAuthorizationID()
298 {
299 return authorizationID;
300 }
301
302
303
304 /**
305 * Retrieves the static password for the bind request, if one was provided.
306 *
307 * @return The static password for the bind request, or {@code null} if no
308 * static password was provided and only the one-time password should
309 * be used for authentication.
310 */
311 public final ASN1OctetString getStaticPassword()
312 {
313 return staticPassword;
314 }
315
316
317
318 /**
319 * {@inheritDoc}
320 */
321 @Override()
322 public final String getSASLMechanismName()
323 {
324 return UNBOUNDID_TOTP_MECHANISM_NAME;
325 }
326
327
328
329 /**
330 * {@inheritDoc}
331 */
332 @Override()
333 protected final BindResult process(final LDAPConnection connection,
334 final int depth)
335 throws LDAPException
336 {
337 messageID = InternalSDKHelper.nextMessageID(connection);
338 return sendBindRequest(connection, "", getSASLCredentials(), getControls(),
339 getResponseTimeoutMillis(connection));
340 }
341
342
343
344 /**
345 * Retrieves the encoded SASL credentials that may be included in an
346 * UNBOUNDID-TOTP SASL bind request.
347 *
348 * @return The encoded SASL credentials that may be included in an
349 * UNBOUNDID-TOTP SASL bind request.
350 *
351 * @throws LDAPException If a problem is encountered while attempting to
352 * obtain the encoded credentials.
353 */
354 protected abstract ASN1OctetString getSASLCredentials()
355 throws LDAPException;
356
357
358
359 /**
360 * Encodes the provided information in a form suitable for inclusion in an
361 * UNBOUNDID-TOTP SASL bind request.
362 *
363 * @param authenticationID The authentication identity for the bind request.
364 * It must not be {@code null}, and must be in the
365 * form "u:" followed by a username, or "dn:"
366 * followed by a DN.
367 * @param authorizationID The authorization identity for the bind request.
368 * It may be {@code null} if the authorization
369 * identity should be the same as the authentication
370 * identity. If an authorization identity is
371 * specified, it must be in the form "u:" followed
372 * by a username, or "dn:" followed by a DN. The
373 * value "dn:" may indicate an authorization
374 * identity of the anonymous user.
375 * @param totpPassword The TOTP password to include in the bind request.
376 * It must not be {@code null}.
377 * @param staticPassword The static password for the target user. It may
378 * be {@code null} if only the one-time password is
379 * to be used for authentication (which may or may
380 * not be allowed by the server).
381 *
382 * @return The encoded SASL credentials.
383 */
384 public static ASN1OctetString encodeCredentials(final String authenticationID,
385 final String authorizationID,
386 final String totpPassword,
387 final ASN1OctetString staticPassword)
388 {
389 Validator.ensureNotNull(authenticationID);
390 Validator.ensureNotNull(totpPassword);
391
392 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(4);
393 elements.add(new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID));
394
395 if (authorizationID != null)
396 {
397 elements.add(new ASN1OctetString(TYPE_AUTHORIZATION_ID, authorizationID));
398 }
399
400 elements.add(new ASN1OctetString(TYPE_TOTP_PASSWORD, totpPassword));
401
402 if (staticPassword != null)
403 {
404 if (staticPassword.getType() == TYPE_STATIC_PASSWORD)
405 {
406 elements.add(staticPassword);
407 }
408 else
409 {
410 elements.add(new ASN1OctetString(TYPE_STATIC_PASSWORD,
411 staticPassword.getValue()));
412 }
413 }
414
415 return new ASN1OctetString(new ASN1Sequence(elements).encode());
416 }
417
418
419
420 /**
421 * {@inheritDoc}
422 */
423 @Override()
424 public final int getLastMessageID()
425 {
426 return messageID;
427 }
428
429
430
431 /**
432 * {@inheritDoc}
433 */
434 @Override()
435 public final void toString(final StringBuilder buffer)
436 {
437 buffer.append("UnboundIDTOTPBindRequest(authID='");
438 buffer.append(authenticationID);
439 buffer.append("', ");
440
441 if (authorizationID != null)
442 {
443 buffer.append("authzID='");
444 buffer.append(authorizationID);
445 buffer.append("', ");
446 }
447
448 buffer.append("includesStaticPassword=");
449 buffer.append(staticPassword != null);
450
451
452 final Control[] controls = getControls();
453 if (controls.length > 0)
454 {
455 buffer.append(", controls={");
456 for (int i=0; i < controls.length; i++)
457 {
458 if (i > 0)
459 {
460 buffer.append(", ");
461 }
462
463 buffer.append(controls[i]);
464 }
465 buffer.append('}');
466 }
467
468 buffer.append(')');
469 }
470 }