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