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