001/* 002 * Copyright 2016-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2016-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.extensions; 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.Control; 031import com.unboundid.ldap.sdk.ExtendedRequest; 032import com.unboundid.ldap.sdk.LDAPConnection; 033import com.unboundid.ldap.sdk.LDAPException; 034import com.unboundid.ldap.sdk.ResultCode; 035import com.unboundid.util.Debug; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.StaticUtils; 038import com.unboundid.util.ThreadSafety; 039import com.unboundid.util.ThreadSafetyLevel; 040import com.unboundid.util.Validator; 041 042import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 043 044 045 046/** 047 * This class provides an implementation of an extended request that may be used 048 * to generate a shared secret for use in generating TOTP authentication codes 049 * (as per <A HREF="http://www.ietf.org/rfc/rfc6238.txt">RFC 6238</A>, for 050 * example, using the mechanism provided in the 051 * {@link com.unboundid.ldap.sdk.unboundidds.OneTimePassword} class), which can 052 * be used to authenticate to the server via the 053 * {@link com.unboundid.ldap.sdk.unboundidds.UnboundIDTOTPBindRequest}. 054 * <BR> 055 * <BLOCKQUOTE> 056 * <B>NOTE:</B> This class, and other classes within the 057 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 058 * supported for use against Ping Identity, UnboundID, and 059 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 060 * for proprietary functionality or for external specifications that are not 061 * considered stable or mature enough to be guaranteed to work in an 062 * interoperable way with other types of LDAP servers. 063 * </BLOCKQUOTE> 064 * <BR> 065 * This request may be invoked in one of following ways: 066 * <BR><BR> 067 * <UL> 068 * <LI> 069 * With a {@code null} authentication identity and a non-{@code null} static 070 * password. In this case, the authorization identity for the operation 071 * (typically the user as whom the underlying connection is authenticated, 072 * but possibly a different user if the request also includes a control like 073 * the proxied authorization or intermediate client request control that 074 * specifies and alternate authorization identity, or if the client 075 * authenticated with a SASL mechanism that included an alternate 076 * authorization identity) will be used as the authentication identity for 077 * this request, and the static password must be valid for that user. This 078 * will be treated as a user requesting a TOTP shared secret for their own 079 * account. 080 * </LI> 081 * <LI> 082 * With a non-{@code null} authentication identity (which may or may not 083 * match the authorization identity for the operation) and a 084 * non-{@code null} static password that is valid for the provided 085 * authentication identity. This will also be treated as a user requesting 086 * a TOTP shared secret for their own account. 087 * </LI> 088 * <LI> 089 * With a non-{@code null} authentication identity and a {@code null} static 090 * password. In this case, the authentication identity must not match the 091 * authorization identity for the operation, and the authorization identity 092 * must have the password-reset privilege. This will be treated as an 093 * administrator requesting a TOTP shared secret on behalf of a user and is 094 * recommended only for the case in which the identity of the user has been 095 * verified through some means other than a static password. 096 * </LI> 097 * </UL> 098 * <BR><BR> 099 * If the request is processed successfully, the server will generate a TOTP 100 * shared secret for the user, will store it in the user's entry, and will 101 * return that secret back to the client via the 102 * {@link GenerateTOTPSharedSecretExtendedResult}. 103 * <BR><BR> 104 * Note that this operation will not interfere with any other TOTP shared 105 * secrets that may already exist in the user's entry; the new shared secret 106 * will be merged with any existing shared secret values for the user. If a 107 * TOTP shared secret is no longer needed, the 108 * {@link RevokeTOTPSharedSecretExtendedRequest} may be used to remove it from 109 * the user's account. 110 * <BR><BR> 111 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.56, and it must 112 * include a request value with the following encoding: 113 * <BR><BR> 114 * <PRE> 115 * GenerateTOTPSharedSecretRequest ::= SEQUENCE { 116 * authenticationID [0] OCTET STRING OPTIONAL, 117 * staticPassword [1] OCTET STRING OPTIONAL, 118 * ... } 119 * </PRE> 120 * 121 */ 122@NotMutable() 123@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 124public final class GenerateTOTPSharedSecretExtendedRequest 125 extends ExtendedRequest 126{ 127 /** 128 * The OID (1.3.6.1.4.1.30221.2.6.56) for the generate TOTP shared secret 129 * extended request. 130 */ 131 public static final String GENERATE_TOTP_SHARED_SECRET_REQUEST_OID = 132 "1.3.6.1.4.1.30221.2.6.56"; 133 134 135 136 /** 137 * The BER type for the authentication ID element of the request value 138 * sequence. 139 */ 140 private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80; 141 142 143 144 /** 145 * The BER type for the static password element of the request value sequence. 146 */ 147 private static final byte TYPE_STATIC_PASSWORD = (byte) 0x81; 148 149 150 151 /** 152 * The serial version UID for this serializable class. 153 */ 154 private static final long serialVersionUID = -1617090986047944957L; 155 156 157 158 // The static password for the request. 159 private final ASN1OctetString staticPassword; 160 161 // The authentication ID for the request. 162 private final String authenticationID; 163 164 165 166 /** 167 * Creates a new generate TOTP shared secret extended request with the 168 * provided information. 169 * 170 * @param authenticationID The authentication ID to use to identify the user 171 * for whom to generate the TOTP shared secret. It 172 * should be a string in the form "dn:" followed by 173 * the DN of the target user, or "u:" followed by 174 * the username. It may be {@code null} if the TOTP 175 * shared secret is to be generated for the 176 * authorization identity for the operation, and 177 * only if the {@code staticPassword} is 178 * non-{@code null}). 179 * @param staticPassword The static password of the user for whom to 180 * generate the TOTP shared secret. It may be 181 * {@code null} only if the {@code authenticationID} 182 * is non-{@code null}, is different from the 183 * operation's authorization identity, and the 184 * operation's authorization identity has the 185 * password-reset privilege. 186 * @param controls The set of controls to include in the request. 187 * It may be {@code null} or empty if there should 188 * not be any request controls. 189 */ 190 public GenerateTOTPSharedSecretExtendedRequest(final String authenticationID, 191 final String staticPassword, 192 final Control... controls) 193 { 194 this(authenticationID, encodePassword(staticPassword), controls); 195 } 196 197 198 199 /** 200 * Creates a new generate TOTP shared secret extended request with the 201 * provided information. 202 * 203 * @param authenticationID The authentication ID to use to identify the user 204 * for whom to generate the TOTP shared secret. It 205 * should be a string in the form "dn:" followed by 206 * the DN of the target user, or "u:" followed by 207 * the username. It may be {@code null} if the TOTP 208 * shared secret is to be generated for the 209 * authorization identity for the operation, and 210 * only if the {@code staticPassword} is 211 * non-{@code null}). 212 * @param staticPassword The static password of the user for whom to 213 * generate the TOTP shared secret. It may be 214 * {@code null} only if the {@code authenticationID} 215 * is non-{@code null}, is different from the 216 * operation's authorization identity, and the 217 * operation's authorization identity has the 218 * password-reset privilege. 219 * @param controls The set of controls to include in the request. 220 * It may be {@code null} or empty if there should 221 * not be any request controls. 222 */ 223 public GenerateTOTPSharedSecretExtendedRequest(final String authenticationID, 224 final byte[] staticPassword, 225 final Control... controls) 226 { 227 this(authenticationID, encodePassword(staticPassword), controls); 228 } 229 230 231 232 /** 233 * Creates a new generate TOTP shared secret extended request with the 234 * provided information. 235 * 236 * @param authenticationID The authentication ID to use to identify the user 237 * for whom to generate the TOTP shared secret. It 238 * should be a string in the form "dn:" followed by 239 * the DN of the target user, or "u:" followed by 240 * the username. It may be {@code null} if the TOTP 241 * shared secret is to be generated for the 242 * authorization identity for the operation, and 243 * only if the {@code staticPassword} is 244 * non-{@code null}). 245 * @param staticPassword The static password of the user for whom to 246 * generate the TOTP shared secret. It may be 247 * {@code null} only if the {@code authenticationID} 248 * is non-{@code null}, is different from the 249 * operation's authorization identity, and the 250 * operation's authorization identity has the 251 * password-reset privilege. 252 * @param controls The set of controls to include in the request. 253 * It may be {@code null} or empty if there should 254 * not be any request controls. 255 */ 256 public GenerateTOTPSharedSecretExtendedRequest(final String authenticationID, 257 final ASN1OctetString staticPassword, final Control... controls) 258 { 259 super(GENERATE_TOTP_SHARED_SECRET_REQUEST_OID, 260 encodeValue(authenticationID, staticPassword), controls); 261 262 this.authenticationID = authenticationID; 263 this.staticPassword = staticPassword; 264 } 265 266 267 268 /** 269 * Creates a new generate TOTP shared secret extended request that is decoded 270 * from the provided generic extended request. 271 * 272 * @param request The generic extended request to decode as a generate TOTP 273 * shared secret request. 274 * 275 * @throws LDAPException If a problem is encountered while attempting to 276 * decode the provided request. 277 */ 278 public GenerateTOTPSharedSecretExtendedRequest(final ExtendedRequest request) 279 throws LDAPException 280 { 281 super(request); 282 283 final ASN1OctetString value = request.getValue(); 284 if (value == null) 285 { 286 throw new LDAPException(ResultCode.DECODING_ERROR, 287 ERR_GEN_TOTP_SECRET_REQUEST_NO_VALUE.get()); 288 } 289 290 try 291 { 292 String authID = null; 293 ASN1OctetString staticPW = null; 294 for (final ASN1Element e : 295 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 296 { 297 switch (e.getType()) 298 { 299 case TYPE_AUTHENTICATION_ID: 300 authID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 301 break; 302 case TYPE_STATIC_PASSWORD: 303 staticPW = ASN1OctetString.decodeAsOctetString(e); 304 break; 305 default: 306 throw new LDAPException(ResultCode.DECODING_ERROR, 307 ERR_GEN_TOTP_SECRET_REQUEST_UNRECOGNIZED_TYPE.get( 308 StaticUtils.toHex(e.getType()))); 309 } 310 } 311 312 if ((authID == null) && (staticPW == null)) 313 { 314 throw new LDAPException(ResultCode.DECODING_ERROR, 315 ERR_GEN_TOTP_SECRET_REQUEST_NEITHER_AUTHN_ID_NOR_PW.get()); 316 } 317 318 authenticationID = authID; 319 staticPassword = staticPW; 320 } 321 catch (final LDAPException le) 322 { 323 Debug.debugException(le); 324 throw le; 325 } 326 catch (final Exception e) 327 { 328 Debug.debugException(e); 329 throw new LDAPException(ResultCode.DECODING_ERROR, 330 ERR_GEN_TOTP_SECRET_REQUEST_ERROR_DECODING_VALUE.get( 331 StaticUtils.getExceptionMessage(e)), 332 e); 333 } 334 } 335 336 337 338 /** 339 * Encodes the provided password as an ASN.1 octet string suitable for 340 * inclusion in the encoded request. 341 * 342 * @param password The password to be encoded. It may be {@code null} if 343 * no password should be included. If it is 344 * non-{@code null}, then it must be a string or a byte 345 * array. 346 * 347 * @return The encoded password, or {@code null} if no password was given. 348 */ 349 private static ASN1OctetString encodePassword(final Object password) 350 { 351 if (password == null) 352 { 353 return null; 354 } 355 else if (password instanceof byte[]) 356 { 357 return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password); 358 } 359 else 360 { 361 return new ASN1OctetString(TYPE_STATIC_PASSWORD, 362 String.valueOf(password)); 363 } 364 } 365 366 367 368 /** 369 * Encodes the provided information into an ASN.1 octet string suitable for 370 * use as the value of this extended request. 371 * 372 * @param authenticationID The authentication ID to use to identify the user 373 * for whom to generate the TOTP shared secret. It 374 * should be a string in the form "dn:" followed by 375 * the DN of the target user, or "u:" followed by 376 * the username. It may be {@code null} if the TOTP 377 * shared secret is to be generated for the 378 * authorization identity for the operation, and 379 * only if the {@code staticPassword} is 380 * non-{@code null}). 381 * @param staticPassword The static password of the user for whom to 382 * generate the TOTP shared secret. It may be 383 * {@code null} only if the {@code authenticationID} 384 * is non-{@code null}, is different from the 385 * operation's authorization identity, and the 386 * operation's authorization identity has the 387 * password-reset privilege. 388 * 389 * @return The ASN.1 octet string containing the encoded request value. 390 */ 391 private static ASN1OctetString encodeValue(final String authenticationID, 392 final ASN1OctetString staticPassword) 393 { 394 if (authenticationID == null) 395 { 396 Validator.ensureTrue((staticPassword != null), 397 "If the authentication ID is null, the static password must be " + 398 "non-null."); 399 } 400 401 final ArrayList<ASN1Element> elements = new ArrayList<>(2); 402 403 if (authenticationID != null) 404 { 405 elements.add( 406 new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID)); 407 } 408 409 if (staticPassword != null) 410 { 411 elements.add(staticPassword); 412 } 413 414 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 415 } 416 417 418 419 /** 420 * Retrieves the authentication ID that identifies the user for whom to 421 * generate the TOTP shared secret, if provided. 422 * 423 * @return The authentication ID that identifies the target user, or 424 * {@code null} if the shared secret is to be generated for the 425 * authorization identity associated with the extended request. 426 */ 427 public String getAuthenticationID() 428 { 429 return authenticationID; 430 } 431 432 433 434 /** 435 * Retrieves the string representation of the static password for the target 436 * user, if provided. 437 * 438 * @return The string representation of the static password for the target 439 * user, or {@code null} if no static password was provided. 440 */ 441 public String getStaticPasswordString() 442 { 443 if (staticPassword == null) 444 { 445 return null; 446 } 447 else 448 { 449 return staticPassword.stringValue(); 450 } 451 } 452 453 454 455 /** 456 * Retrieves the bytes that comprise the static password for the target user, 457 * if provided. 458 * 459 * @return The bytes that comprise the static password for the target user, 460 * or {@code null} if no static password was provided. 461 */ 462 public byte[] getStaticPasswordBytes() 463 { 464 if (staticPassword == null) 465 { 466 return null; 467 } 468 else 469 { 470 return staticPassword.getValue(); 471 } 472 } 473 474 475 476 /** 477 * {@inheritDoc} 478 */ 479 @Override() 480 protected GenerateTOTPSharedSecretExtendedResult process( 481 final LDAPConnection connection, final int depth) 482 throws LDAPException 483 { 484 return new GenerateTOTPSharedSecretExtendedResult( 485 super.process(connection, depth)); 486 } 487 488 489 490 /** 491 * {@inheritDoc} 492 */ 493 @Override() 494 public GenerateTOTPSharedSecretExtendedRequest duplicate() 495 { 496 return duplicate(getControls()); 497 } 498 499 500 501 /** 502 * {@inheritDoc} 503 */ 504 @Override() 505 public GenerateTOTPSharedSecretExtendedRequest duplicate( 506 final Control[] controls) 507 { 508 final GenerateTOTPSharedSecretExtendedRequest r = 509 new GenerateTOTPSharedSecretExtendedRequest(authenticationID, 510 staticPassword, controls); 511 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 512 return r; 513 } 514 515 516 517 /** 518 * {@inheritDoc} 519 */ 520 @Override() 521 public String getExtendedRequestName() 522 { 523 return INFO_GEN_TOTP_SECRET_REQUEST_NAME.get(); 524 } 525 526 527 528 /** 529 * {@inheritDoc} 530 */ 531 @Override() 532 public void toString(final StringBuilder buffer) 533 { 534 buffer.append("GenerateTOTPSharedSecretExtendedRequest("); 535 536 if (authenticationID != null) 537 { 538 buffer.append("authenticationID='"); 539 buffer.append(authenticationID); 540 buffer.append("', "); 541 } 542 543 buffer.append("staticPasswordProvided="); 544 buffer.append(staticPassword != null); 545 546 final Control[] controls = getControls(); 547 if (controls.length > 0) 548 { 549 buffer.append(", controls={"); 550 for (int i=0; i < controls.length; i++) 551 { 552 if (i > 0) 553 { 554 buffer.append(", "); 555 } 556 557 buffer.append(controls[i]); 558 } 559 buffer.append('}'); 560 } 561 562 buffer.append(')'); 563 } 564}