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 }