001/* 002 * Copyright 2009-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-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) 2009-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; 037 038 039 040import java.io.File; 041import java.io.FileWriter; 042import java.io.PrintWriter; 043import java.security.PrivilegedExceptionAction; 044import java.util.ArrayList; 045import java.util.HashMap; 046import java.util.List; 047import java.util.Set; 048import java.util.concurrent.atomic.AtomicReference; 049import java.util.logging.Level; 050import javax.security.auth.Subject; 051import javax.security.auth.callback.Callback; 052import javax.security.auth.callback.CallbackHandler; 053import javax.security.auth.callback.NameCallback; 054import javax.security.auth.callback.PasswordCallback; 055import javax.security.auth.callback.UnsupportedCallbackException; 056import javax.security.auth.login.Configuration; 057import javax.security.auth.login.LoginContext; 058import javax.security.sasl.RealmCallback; 059import javax.security.sasl.Sasl; 060import javax.security.sasl.SaslClient; 061 062import com.unboundid.asn1.ASN1OctetString; 063import com.unboundid.util.Debug; 064import com.unboundid.util.DebugType; 065import com.unboundid.util.InternalUseOnly; 066import com.unboundid.util.NotMutable; 067import com.unboundid.util.NotNull; 068import com.unboundid.util.Nullable; 069import com.unboundid.util.StaticUtils; 070import com.unboundid.util.ThreadSafety; 071import com.unboundid.util.ThreadSafetyLevel; 072import com.unboundid.util.Validator; 073 074import static com.unboundid.ldap.sdk.LDAPMessages.*; 075 076 077 078/** 079 * This class provides a SASL GSSAPI bind request implementation as described in 080 * <A HREF="http://www.ietf.org/rfc/rfc4752.txt">RFC 4752</A>. It provides the 081 * ability to authenticate to a directory server using Kerberos V, which can 082 * serve as a kind of single sign-on mechanism that may be shared across 083 * client applications that support Kerberos. 084 * <BR><BR> 085 * This class uses the Java Authentication and Authorization Service (JAAS) 086 * behind the scenes to perform all Kerberos processing. This framework 087 * requires a configuration file to indicate the underlying mechanism to be 088 * used. It is possible for clients to explicitly specify the path to the 089 * configuration file that should be used, but if none is given then a default 090 * file will be created and used. This default file should be sufficient for 091 * Sun-provided JVMs, but a custom file may be required for JVMs provided by 092 * other vendors. 093 * <BR><BR> 094 * Elements included in a GSSAPI bind request include: 095 * <UL> 096 * <LI>Authentication ID -- A string which identifies the user that is 097 * attempting to authenticate. It should be the user's Kerberos 098 * principal.</LI> 099 * <LI>Authorization ID -- An optional string which specifies an alternate 100 * authorization identity that should be used for subsequent operations 101 * requested on the connection. Like the authentication ID, the 102 * authorization ID should be a Kerberos principal.</LI> 103 * <LI>KDC Address -- An optional string which specifies the IP address or 104 * resolvable name for the Kerberos key distribution center. If this is 105 * not provided, an attempt will be made to determine the appropriate 106 * value from the system configuration.</LI> 107 * <LI>Realm -- An optional string which specifies the realm into which the 108 * user should authenticate. If this is not provided, an attempt will be 109 * made to determine the appropriate value from the system 110 * configuration</LI> 111 * <LI>Password -- The clear-text password for the target user in the Kerberos 112 * realm.</LI> 113 * </UL> 114 * <H2>Example</H2> 115 * The following example demonstrates the process for performing a GSSAPI bind 116 * against a directory server with a username of "john.doe" and a password 117 * of "password": 118 * <PRE> 119 * GSSAPIBindRequestProperties gssapiProperties = 120 * new GSSAPIBindRequestProperties("john.doe@EXAMPLE.COM", "password"); 121 * gssapiProperties.setKDCAddress("kdc.example.com"); 122 * gssapiProperties.setRealm("EXAMPLE.COM"); 123 * 124 * GSSAPIBindRequest bindRequest = 125 * new GSSAPIBindRequest(gssapiProperties); 126 * BindResult bindResult; 127 * try 128 * { 129 * bindResult = connection.bind(bindRequest); 130 * // If we get here, then the bind was successful. 131 * } 132 * catch (LDAPException le) 133 * { 134 * // The bind failed for some reason. 135 * bindResult = new BindResult(le.toLDAPResult()); 136 * ResultCode resultCode = le.getResultCode(); 137 * String errorMessageFromServer = le.getDiagnosticMessage(); 138 * } 139 * </PRE> 140 */ 141@NotMutable() 142@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 143public final class GSSAPIBindRequest 144 extends SASLBindRequest 145 implements CallbackHandler, PrivilegedExceptionAction<Object> 146{ 147 /** 148 * The name for the GSSAPI SASL mechanism. 149 */ 150 @NotNull public static final String GSSAPI_MECHANISM_NAME = "GSSAPI"; 151 152 153 154 /** 155 * The name of the configuration property used to specify the address of the 156 * Kerberos key distribution center. 157 */ 158 @NotNull private static final String PROPERTY_KDC_ADDRESS = 159 "java.security.krb5.kdc"; 160 161 162 163 /** 164 * The name of the configuration property used to specify the Kerberos realm. 165 */ 166 @NotNull private static final String PROPERTY_REALM = 167 "java.security.krb5.realm"; 168 169 170 171 /** 172 * The name of the configuration property used to specify the path to the JAAS 173 * configuration file. 174 */ 175 @NotNull private static final String PROPERTY_CONFIG_FILE = 176 "java.security.auth.login.config"; 177 178 179 180 /** 181 * The name of the configuration property used to indicate whether credentials 182 * can come from somewhere other than the location specified in the JAAS 183 * configuration file. 184 */ 185 @NotNull private static final String PROPERTY_SUBJECT_CREDS_ONLY = 186 "javax.security.auth.useSubjectCredsOnly"; 187 188 189 190 /** 191 * The value for the java.security.auth.login.config property at the time that 192 * this class was loaded. If this is set, then it will be used in place of 193 * an automatically-generated config file. 194 */ 195 @Nullable private static final String DEFAULT_CONFIG_FILE = 196 StaticUtils.getSystemProperty(PROPERTY_CONFIG_FILE); 197 198 199 200 /** 201 * The default KDC address that will be used if none is explicitly configured. 202 */ 203 @Nullable private static final String DEFAULT_KDC_ADDRESS = 204 StaticUtils.getSystemProperty(PROPERTY_KDC_ADDRESS); 205 206 207 208 /** 209 * The default realm that will be used if none is explicitly configured. 210 */ 211 @Nullable private static final String DEFAULT_REALM = 212 StaticUtils.getSystemProperty(PROPERTY_REALM); 213 214 215 216 /** 217 * The serial version UID for this serializable class. 218 */ 219 private static final long serialVersionUID = 2511890818146955112L; 220 221 222 223 // The password for the GSSAPI bind request. 224 @Nullable private final ASN1OctetString password; 225 226 // A reference to the connection to use for bind processing. 227 @NotNull private final AtomicReference<LDAPConnection> conn; 228 229 // Indicates whether to enable JVM-level debugging for GSSAPI processing. 230 private final boolean enableGSSAPIDebugging; 231 232 // Indicates whether the client should act as the GSSAPI initiator or the 233 // acceptor. 234 @Nullable private final Boolean isInitiator; 235 236 // Indicates whether to attempt to refresh the configuration before the JAAS 237 // login method is called. 238 private final boolean refreshKrb5Config; 239 240 // Indicates whether to attempt to renew the client's existing ticket-granting 241 // ticket if authentication uses an existing Kerberos session. 242 private final boolean renewTGT; 243 244 // Indicates whether to require that the credentials be obtained from the 245 // ticket cache such that authentication will fail if the client does not have 246 // an existing Kerberos session. 247 private final boolean requireCachedCredentials; 248 249 // Indicates whether to allow the to obtain the credentials to be obtained 250 // from a keytab. 251 private final boolean useKeyTab; 252 253 // Indicates whether to allow the client to use credentials that are outside 254 // of the current subject. 255 private final boolean useSubjectCredentialsOnly; 256 257 // Indicates whether to enable the use pf a ticket cache. 258 private final boolean useTicketCache; 259 260 // The message ID from the last LDAP message sent from this request. 261 private int messageID; 262 263 // The SASL quality of protection value(s) allowed for the DIGEST-MD5 bind 264 // request. 265 @NotNull private final List<SASLQualityOfProtection> allowedQoP; 266 267 // A list that will be updated with messages about any unhandled callbacks 268 // encountered during processing. 269 @NotNull private final List<String> unhandledCallbackMessages; 270 271 // The names of any system properties that should not be altered by GSSAPI 272 // processing. 273 @NotNull private Set<String> suppressedSystemProperties; 274 275 // The authentication ID string for the GSSAPI bind request. 276 @Nullable private final String authenticationID; 277 278 // The authorization ID string for the GSSAPI bind request, if available. 279 @Nullable private final String authorizationID; 280 281 // The path to the JAAS configuration file to use for bind processing. 282 @Nullable private final String configFilePath; 283 284 // The name that will be used to identify this client in the JAAS framework. 285 @NotNull private final String jaasClientName; 286 287 // The KDC address for the GSSAPI bind request, if available. 288 @Nullable private final String kdcAddress; 289 290 // The path to the keytab file to use if useKeyTab is true. 291 @Nullable private final String keyTabPath; 292 293 // The realm for the GSSAPI bind request, if available. 294 @Nullable private final String realm; 295 296 // The server name that should be used when creating the Java SaslClient, if 297 // defined. 298 @Nullable private final String saslClientServerName; 299 300 // The protocol that should be used in the Kerberos service principal for 301 // the server system. 302 @NotNull private final String servicePrincipalProtocol; 303 304 // The path to the Kerberos ticket cache to use. 305 @Nullable private final String ticketCachePath; 306 307 308 309 /** 310 * Creates a new SASL GSSAPI bind request with the provided authentication ID 311 * and password. 312 * 313 * @param authenticationID The authentication ID for this bind request. It 314 * must not be {@code null}. 315 * @param password The password for this bind request. It must not 316 * be {@code null}. 317 * 318 * @throws LDAPException If a problem occurs while creating the JAAS 319 * configuration file to use during authentication 320 * processing. 321 */ 322 public GSSAPIBindRequest(@NotNull final String authenticationID, 323 @NotNull final String password) 324 throws LDAPException 325 { 326 this(new GSSAPIBindRequestProperties(authenticationID, password)); 327 } 328 329 330 331 /** 332 * Creates a new SASL GSSAPI bind request with the provided authentication ID 333 * and password. 334 * 335 * @param authenticationID The authentication ID for this bind request. It 336 * must not be {@code null}. 337 * @param password The password for this bind request. It must not 338 * be {@code null}. 339 * 340 * @throws LDAPException If a problem occurs while creating the JAAS 341 * configuration file to use during authentication 342 * processing. 343 */ 344 public GSSAPIBindRequest(@NotNull final String authenticationID, 345 @NotNull final byte[] password) 346 throws LDAPException 347 { 348 this(new GSSAPIBindRequestProperties(authenticationID, password)); 349 } 350 351 352 353 /** 354 * Creates a new SASL GSSAPI bind request with the provided authentication ID 355 * and password. 356 * 357 * @param authenticationID The authentication ID for this bind request. It 358 * must not be {@code null}. 359 * @param password The password for this bind request. It must not 360 * be {@code null}. 361 * @param controls The set of controls to include in the request. 362 * 363 * @throws LDAPException If a problem occurs while creating the JAAS 364 * configuration file to use during authentication 365 * processing. 366 */ 367 public GSSAPIBindRequest(@NotNull final String authenticationID, 368 @NotNull final String password, 369 @Nullable final Control[] controls) 370 throws LDAPException 371 { 372 this(new GSSAPIBindRequestProperties(authenticationID, password), controls); 373 } 374 375 376 377 /** 378 * Creates a new SASL GSSAPI bind request with the provided authentication ID 379 * and password. 380 * 381 * @param authenticationID The authentication ID for this bind request. It 382 * must not be {@code null}. 383 * @param password The password for this bind request. It must not 384 * be {@code null}. 385 * @param controls The set of controls to include in the request. 386 * 387 * @throws LDAPException If a problem occurs while creating the JAAS 388 * configuration file to use during authentication 389 * processing. 390 */ 391 public GSSAPIBindRequest(@NotNull final String authenticationID, 392 @NotNull final byte[] password, 393 @Nullable final Control[] controls) 394 throws LDAPException 395 { 396 this(new GSSAPIBindRequestProperties(authenticationID, password), controls); 397 } 398 399 400 401 /** 402 * Creates a new SASL GSSAPI bind request with the provided information. 403 * 404 * @param authenticationID The authentication ID for this bind request. It 405 * must not be {@code null}. 406 * @param authorizationID The authorization ID for this bind request. It 407 * may be {@code null} if no alternate authorization 408 * ID should be used. 409 * @param password The password for this bind request. It must not 410 * be {@code null}. 411 * @param realm The realm to use for the authentication. It may 412 * be {@code null} to attempt to use the default 413 * realm from the system configuration. 414 * @param kdcAddress The address of the Kerberos key distribution 415 * center. It may be {@code null} to attempt to use 416 * the default KDC from the system configuration. 417 * @param configFilePath The path to the JAAS configuration file to use 418 * for the authentication processing. It may be 419 * {@code null} to use the default JAAS 420 * configuration. 421 * 422 * @throws LDAPException If a problem occurs while creating the JAAS 423 * configuration file to use during authentication 424 * processing. 425 */ 426 public GSSAPIBindRequest(@NotNull final String authenticationID, 427 @Nullable final String authorizationID, 428 @NotNull final String password, 429 @Nullable final String realm, 430 @Nullable final String kdcAddress, 431 @Nullable final String configFilePath) 432 throws LDAPException 433 { 434 this(new GSSAPIBindRequestProperties(authenticationID, authorizationID, 435 new ASN1OctetString(password), realm, kdcAddress, configFilePath)); 436 } 437 438 439 440 /** 441 * Creates a new SASL GSSAPI bind request with the provided information. 442 * 443 * @param authenticationID The authentication ID for this bind request. It 444 * must not be {@code null}. 445 * @param authorizationID The authorization ID for this bind request. It 446 * may be {@code null} if no alternate authorization 447 * ID should be used. 448 * @param password The password for this bind request. It must not 449 * be {@code null}. 450 * @param realm The realm to use for the authentication. It may 451 * be {@code null} to attempt to use the default 452 * realm from the system configuration. 453 * @param kdcAddress The address of the Kerberos key distribution 454 * center. It may be {@code null} to attempt to use 455 * the default KDC from the system configuration. 456 * @param configFilePath The path to the JAAS configuration file to use 457 * for the authentication processing. It may be 458 * {@code null} to use the default JAAS 459 * configuration. 460 * 461 * @throws LDAPException If a problem occurs while creating the JAAS 462 * configuration file to use during authentication 463 * processing. 464 */ 465 public GSSAPIBindRequest(@NotNull final String authenticationID, 466 @Nullable final String authorizationID, 467 @NotNull final byte[] password, 468 @Nullable final String realm, 469 @Nullable final String kdcAddress, 470 @Nullable final String configFilePath) 471 throws LDAPException 472 { 473 this(new GSSAPIBindRequestProperties(authenticationID, authorizationID, 474 new ASN1OctetString(password), realm, kdcAddress, configFilePath)); 475 } 476 477 478 479 /** 480 * Creates a new SASL GSSAPI bind request with the provided information. 481 * 482 * @param authenticationID The authentication ID for this bind request. It 483 * must not be {@code null}. 484 * @param authorizationID The authorization ID for this bind request. It 485 * may be {@code null} if no alternate authorization 486 * ID should be used. 487 * @param password The password for this bind request. It must not 488 * be {@code null}. 489 * @param realm The realm to use for the authentication. It may 490 * be {@code null} to attempt to use the default 491 * realm from the system configuration. 492 * @param kdcAddress The address of the Kerberos key distribution 493 * center. It may be {@code null} to attempt to use 494 * the default KDC from the system configuration. 495 * @param configFilePath The path to the JAAS configuration file to use 496 * for the authentication processing. It may be 497 * {@code null} to use the default JAAS 498 * configuration. 499 * @param controls The set of controls to include in the request. 500 * 501 * @throws LDAPException If a problem occurs while creating the JAAS 502 * configuration file to use during authentication 503 * processing. 504 */ 505 public GSSAPIBindRequest(@NotNull final String authenticationID, 506 @Nullable final String authorizationID, 507 @NotNull final String password, 508 @Nullable final String realm, 509 @Nullable final String kdcAddress, 510 @Nullable final String configFilePath, 511 @Nullable final Control[] controls) 512 throws LDAPException 513 { 514 this(new GSSAPIBindRequestProperties(authenticationID, authorizationID, 515 new ASN1OctetString(password), realm, kdcAddress, configFilePath), 516 controls); 517 } 518 519 520 521 /** 522 * Creates a new SASL GSSAPI bind request with the provided information. 523 * 524 * @param authenticationID The authentication ID for this bind request. It 525 * must not be {@code null}. 526 * @param authorizationID The authorization ID for this bind request. It 527 * may be {@code null} if no alternate authorization 528 * ID should be used. 529 * @param password The password for this bind request. It must not 530 * be {@code null}. 531 * @param realm The realm to use for the authentication. It may 532 * be {@code null} to attempt to use the default 533 * realm from the system configuration. 534 * @param kdcAddress The address of the Kerberos key distribution 535 * center. It may be {@code null} to attempt to use 536 * the default KDC from the system configuration. 537 * @param configFilePath The path to the JAAS configuration file to use 538 * for the authentication processing. It may be 539 * {@code null} to use the default JAAS 540 * configuration. 541 * @param controls The set of controls to include in the request. 542 * 543 * @throws LDAPException If a problem occurs while creating the JAAS 544 * configuration file to use during authentication 545 * processing. 546 */ 547 public GSSAPIBindRequest(@NotNull final String authenticationID, 548 @Nullable final String authorizationID, 549 @NotNull final byte[] password, 550 @Nullable final String realm, 551 @Nullable final String kdcAddress, 552 @Nullable final String configFilePath, 553 @Nullable final Control[] controls) 554 throws LDAPException 555 { 556 this(new GSSAPIBindRequestProperties(authenticationID, authorizationID, 557 new ASN1OctetString(password), realm, kdcAddress, configFilePath), 558 controls); 559 } 560 561 562 563 /** 564 * Creates a new SASL GSSAPI bind request with the provided set of properties. 565 * 566 * @param gssapiProperties The set of properties that should be used for 567 * the GSSAPI bind request. It must not be 568 * {@code null}. 569 * @param controls The set of controls to include in the request. 570 * 571 * @throws LDAPException If a problem occurs while creating the JAAS 572 * configuration file to use during authentication 573 * processing. 574 */ 575 public GSSAPIBindRequest( 576 @NotNull final GSSAPIBindRequestProperties gssapiProperties, 577 @Nullable final Control... controls) 578 throws LDAPException 579 { 580 super(controls); 581 582 Validator.ensureNotNull(gssapiProperties); 583 584 authenticationID = gssapiProperties.getAuthenticationID(); 585 password = gssapiProperties.getPassword(); 586 realm = gssapiProperties.getRealm(); 587 allowedQoP = gssapiProperties.getAllowedQoP(); 588 kdcAddress = gssapiProperties.getKDCAddress(); 589 jaasClientName = gssapiProperties.getJAASClientName(); 590 saslClientServerName = gssapiProperties.getSASLClientServerName(); 591 servicePrincipalProtocol = gssapiProperties.getServicePrincipalProtocol(); 592 enableGSSAPIDebugging = gssapiProperties.enableGSSAPIDebugging(); 593 useKeyTab = gssapiProperties.useKeyTab(); 594 useSubjectCredentialsOnly = gssapiProperties.useSubjectCredentialsOnly(); 595 useTicketCache = gssapiProperties.useTicketCache(); 596 requireCachedCredentials = gssapiProperties.requireCachedCredentials(); 597 refreshKrb5Config = gssapiProperties.refreshKrb5Config(); 598 renewTGT = gssapiProperties.renewTGT(); 599 keyTabPath = gssapiProperties.getKeyTabPath(); 600 ticketCachePath = gssapiProperties.getTicketCachePath(); 601 isInitiator = gssapiProperties.getIsInitiator(); 602 suppressedSystemProperties = 603 gssapiProperties.getSuppressedSystemProperties(); 604 605 unhandledCallbackMessages = new ArrayList<>(5); 606 607 conn = new AtomicReference<>(); 608 messageID = -1; 609 610 final String authzID = gssapiProperties.getAuthorizationID(); 611 if (authzID == null) 612 { 613 authorizationID = null; 614 } 615 else 616 { 617 authorizationID = authzID; 618 } 619 620 final String cfgPath = gssapiProperties.getConfigFilePath(); 621 if (cfgPath == null) 622 { 623 if (DEFAULT_CONFIG_FILE == null) 624 { 625 configFilePath = getConfigFilePath(gssapiProperties); 626 } 627 else 628 { 629 configFilePath = DEFAULT_CONFIG_FILE; 630 } 631 } 632 else 633 { 634 configFilePath = cfgPath; 635 } 636 } 637 638 639 640 /** 641 * {@inheritDoc} 642 */ 643 @Override() 644 @NotNull() 645 public String getSASLMechanismName() 646 { 647 return GSSAPI_MECHANISM_NAME; 648 } 649 650 651 652 /** 653 * Retrieves the authentication ID for the GSSAPI bind request, if defined. 654 * 655 * @return The authentication ID for the GSSAPI bind request, or {@code null} 656 * if an existing Kerberos session should be used. 657 */ 658 @Nullable() 659 public String getAuthenticationID() 660 { 661 return authenticationID; 662 } 663 664 665 666 /** 667 * Retrieves the authorization ID for this bind request, if any. 668 * 669 * @return The authorization ID for this bind request, or {@code null} if 670 * there should not be a separate authorization identity. 671 */ 672 @Nullable() 673 public String getAuthorizationID() 674 { 675 return authorizationID; 676 } 677 678 679 680 /** 681 * Retrieves the string representation of the password for this bind request, 682 * if defined. 683 * 684 * @return The string representation of the password for this bind request, 685 * or {@code null} if an existing Kerberos session should be used. 686 */ 687 @Nullable() 688 public String getPasswordString() 689 { 690 if (password == null) 691 { 692 return null; 693 } 694 else 695 { 696 return password.stringValue(); 697 } 698 } 699 700 701 702 /** 703 * Retrieves the bytes that comprise the the password for this bind request, 704 * if defined. 705 * 706 * @return The bytes that comprise the password for this bind request, or 707 * {@code null} if an existing Kerberos session should be used. 708 */ 709 @Nullable() 710 public byte[] getPasswordBytes() 711 { 712 if (password == null) 713 { 714 return null; 715 } 716 else 717 { 718 return password.getValue(); 719 } 720 } 721 722 723 724 /** 725 * Retrieves the realm for this bind request, if any. 726 * 727 * @return The realm for this bind request, or {@code null} if none was 728 * defined and the client should attempt to determine the realm from 729 * the system configuration. 730 */ 731 @Nullable() 732 public String getRealm() 733 { 734 return realm; 735 } 736 737 738 739 /** 740 * Retrieves the list of allowed qualities of protection that may be used for 741 * communication that occurs on the connection after the authentication has 742 * completed, in order from most preferred to least preferred. 743 * 744 * @return The list of allowed qualities of protection that may be used for 745 * communication that occurs on the connection after the 746 * authentication has completed, in order from most preferred to 747 * least preferred. 748 */ 749 @NotNull() 750 public List<SASLQualityOfProtection> getAllowedQoP() 751 { 752 return allowedQoP; 753 } 754 755 756 757 /** 758 * Retrieves the address of the Kerberos key distribution center. 759 * 760 * @return The address of the Kerberos key distribution center, or 761 * {@code null} if none was defined and the client should attempt to 762 * determine the KDC address from the system configuration. 763 */ 764 @Nullable() 765 public String getKDCAddress() 766 { 767 return kdcAddress; 768 } 769 770 771 772 /** 773 * Retrieves the path to the JAAS configuration file that will be used during 774 * authentication processing. 775 * 776 * @return The path to the JAAS configuration file that will be used during 777 * authentication processing. 778 */ 779 @Nullable() 780 public String getConfigFilePath() 781 { 782 return configFilePath; 783 } 784 785 786 787 /** 788 * Retrieves the protocol specified in the service principal that the 789 * directory server uses for its communication with the KDC. 790 * 791 * @return The protocol specified in the service principal that the directory 792 * server uses for its communication with the KDC. 793 */ 794 @NotNull() 795 public String getServicePrincipalProtocol() 796 { 797 return servicePrincipalProtocol; 798 } 799 800 801 802 /** 803 * Indicates whether to refresh the configuration before the JAAS 804 * {@code login} method is called. 805 * 806 * @return {@code true} if the GSSAPI implementation should refresh the 807 * configuration before the JAAS {@code login} method is called, or 808 * {@code false} if not. 809 */ 810 public boolean refreshKrb5Config() 811 { 812 return refreshKrb5Config; 813 } 814 815 816 817 /** 818 * Indicates whether to use a keytab to obtain the user credentials. 819 * 820 * @return {@code true} if the GSSAPI login attempt should use a keytab to 821 * obtain the user credentials, or {@code false} if not. 822 */ 823 public boolean useKeyTab() 824 { 825 return useKeyTab; 826 } 827 828 829 830 /** 831 * Retrieves the path to the keytab file from which to obtain the user 832 * credentials. This will only be used if {@link #useKeyTab} returns 833 * {@code true}. 834 * 835 * @return The path to the keytab file from which to obtain the user 836 * credentials, or {@code null} if the default keytab location should 837 * be used. 838 */ 839 @Nullable() 840 public String getKeyTabPath() 841 { 842 return keyTabPath; 843 } 844 845 846 847 /** 848 * Indicates whether to enable the use of a ticket cache to to avoid the need 849 * to supply credentials if the client already has an existing Kerberos 850 * session. 851 * 852 * @return {@code true} if a ticket cache may be used to take advantage of an 853 * existing Kerberos session, or {@code false} if Kerberos 854 * credentials should always be provided. 855 */ 856 public boolean useTicketCache() 857 { 858 return useTicketCache; 859 } 860 861 862 863 /** 864 * Indicates whether GSSAPI authentication should only occur using an existing 865 * Kerberos session. 866 * 867 * @return {@code true} if GSSAPI authentication should only use an existing 868 * Kerberos session and should fail if the client does not have an 869 * existing session, or {@code false} if the client will be allowed 870 * to create a new session if one does not already exist. 871 */ 872 public boolean requireCachedCredentials() 873 { 874 return requireCachedCredentials; 875 } 876 877 878 879 /** 880 * Retrieves the path to the Kerberos ticket cache file that should be used 881 * during authentication, if defined. 882 * 883 * @return The path to the Kerberos ticket cache file that should be used 884 * during authentication, or {@code null} if the default ticket cache 885 * file should be used. 886 */ 887 @Nullable() 888 public String getTicketCachePath() 889 { 890 return ticketCachePath; 891 } 892 893 894 895 /** 896 * Indicates whether to attempt to renew the client's ticket-granting ticket 897 * (TGT) if an existing Kerberos session is used to authenticate. 898 * 899 * @return {@code true} if the client should attempt to renew its 900 * ticket-granting ticket if the authentication is processed using an 901 * existing Kerberos session, or {@code false} if not. 902 */ 903 public boolean renewTGT() 904 { 905 return renewTGT; 906 } 907 908 909 910 /** 911 * Indicates whether to allow the client to use credentials that are outside 912 * of the current subject, obtained via some system-specific mechanism. 913 * 914 * @return {@code true} if the client will only be allowed to use credentials 915 * that are within the current subject, or {@code false} if the 916 * client will be allowed to use credentials outside the current 917 * subject. 918 */ 919 public boolean useSubjectCredentialsOnly() 920 { 921 return useSubjectCredentialsOnly; 922 } 923 924 925 926 /** 927 * Indicates whether the client should be configured so that it explicitly 928 * indicates whether it is the initiator or the acceptor. 929 * 930 * @return {@code Boolean.TRUE} if the client should explicitly indicate that 931 * it is the GSSAPI initiator, {@code Boolean.FALSE} if the client 932 * should explicitly indicate that it is the GSSAPI acceptor, or 933 * {@code null} if the client should not explicitly indicate either 934 * state (which is the default behavior unless the 935 * {@link GSSAPIBindRequestProperties#setIsInitiator} method has 936 * been used to explicitly specify a value). 937 */ 938 @Nullable() 939 public Boolean getIsInitiator() 940 { 941 return isInitiator; 942 } 943 944 945 946 /** 947 * Retrieves a set of system properties that will not be altered by GSSAPI 948 * processing. 949 * 950 * @return A set of system properties that will not be altered by GSSAPI 951 * processing. 952 */ 953 @NotNull() 954 public Set<String> getSuppressedSystemProperties() 955 { 956 return suppressedSystemProperties; 957 } 958 959 960 961 /** 962 * Indicates whether JVM-level debugging should be enabled for GSSAPI bind 963 * processing. 964 * 965 * @return {@code true} if JVM-level debugging should be enabled for GSSAPI 966 * bind processing, or {@code false} if not. 967 */ 968 public boolean enableGSSAPIDebugging() 969 { 970 return enableGSSAPIDebugging; 971 } 972 973 974 975 /** 976 * Retrieves the path to the default JAAS configuration file that will be used 977 * if no file was explicitly provided. A new file may be created if 978 * necessary. 979 * 980 * @param properties The GSSAPI properties that should be used for 981 * authentication. 982 * 983 * @return The path to the default JAAS configuration file that will be used 984 * if no file was explicitly provided. 985 * 986 * @throws LDAPException If an error occurs while attempting to create the 987 * configuration file. 988 */ 989 @NotNull() 990 private static String getConfigFilePath( 991 @NotNull final GSSAPIBindRequestProperties properties) 992 throws LDAPException 993 { 994 try 995 { 996 final File f = 997 File.createTempFile("GSSAPIBindRequest-JAAS-Config-", ".conf"); 998 f.deleteOnExit(); 999 final PrintWriter w = new PrintWriter(new FileWriter(f)); 1000 1001 try 1002 { 1003 // The JAAS configuration file may vary based on the JVM that we're 1004 // using. For Sun-based JVMs, the module will be 1005 // "com.sun.security.auth.module.Krb5LoginModule". 1006 try 1007 { 1008 final Class<?> sunModuleClass = 1009 Class.forName("com.sun.security.auth.module.Krb5LoginModule"); 1010 if (sunModuleClass != null) 1011 { 1012 writeSunJAASConfig(w, properties); 1013 return f.getAbsolutePath(); 1014 } 1015 } 1016 catch (final ClassNotFoundException cnfe) 1017 { 1018 // This is fine. 1019 Debug.debugException(cnfe); 1020 } 1021 1022 1023 // For the IBM JVMs, the module will be 1024 // "com.ibm.security.auth.module.Krb5LoginModule". 1025 try 1026 { 1027 final Class<?> ibmModuleClass = 1028 Class.forName("com.ibm.security.auth.module.Krb5LoginModule"); 1029 if (ibmModuleClass != null) 1030 { 1031 writeIBMJAASConfig(w, properties); 1032 return f.getAbsolutePath(); 1033 } 1034 } 1035 catch (final ClassNotFoundException cnfe) 1036 { 1037 // This is fine. 1038 Debug.debugException(cnfe); 1039 } 1040 1041 1042 // If we've gotten here, then we can't generate an appropriate 1043 // configuration. 1044 throw new LDAPException(ResultCode.LOCAL_ERROR, 1045 ERR_GSSAPI_CANNOT_CREATE_JAAS_CONFIG.get( 1046 ERR_GSSAPI_NO_SUPPORTED_JAAS_MODULE.get())); 1047 } 1048 finally 1049 { 1050 w.close(); 1051 } 1052 } 1053 catch (final LDAPException le) 1054 { 1055 Debug.debugException(le); 1056 throw le; 1057 } 1058 catch (final Exception e) 1059 { 1060 Debug.debugException(e); 1061 1062 throw new LDAPException(ResultCode.LOCAL_ERROR, 1063 ERR_GSSAPI_CANNOT_CREATE_JAAS_CONFIG.get( 1064 StaticUtils.getExceptionMessage(e)), 1065 e); 1066 } 1067 } 1068 1069 1070 1071 /** 1072 * Writes a JAAS configuration file in a form appropriate for Sun VMs. 1073 * 1074 * @param w The writer to use to create the config file. 1075 * @param p The properties to use for GSSAPI authentication. 1076 */ 1077 private static void writeSunJAASConfig(@NotNull final PrintWriter w, 1078 @NotNull final GSSAPIBindRequestProperties p) 1079 { 1080 w.println(p.getJAASClientName() + " {"); 1081 w.println(" com.sun.security.auth.module.Krb5LoginModule required"); 1082 w.println(" client=true"); 1083 1084 if (p.getIsInitiator() != null) 1085 { 1086 w.println(" isInitiator=" + p.getIsInitiator()); 1087 } 1088 1089 if (p.refreshKrb5Config()) 1090 { 1091 w.println(" refreshKrb5Config=true"); 1092 } 1093 1094 if (p.useKeyTab()) 1095 { 1096 w.println(" useKeyTab=true"); 1097 if (p.getKeyTabPath() != null) 1098 { 1099 w.println(" keyTab=\"" + p.getKeyTabPath() + '"'); 1100 } 1101 } 1102 1103 if (p.useTicketCache()) 1104 { 1105 w.println(" useTicketCache=true"); 1106 w.println(" renewTGT=" + p.renewTGT()); 1107 w.println(" doNotPrompt=" + p.requireCachedCredentials()); 1108 1109 final String ticketCachePath = p.getTicketCachePath(); 1110 if (ticketCachePath != null) 1111 { 1112 w.println(" ticketCache=\"" + ticketCachePath + '"'); 1113 } 1114 } 1115 else 1116 { 1117 w.println(" useTicketCache=false"); 1118 } 1119 1120 if (p.enableGSSAPIDebugging()) 1121 { 1122 w.println(" debug=true"); 1123 } 1124 1125 w.println(" ;"); 1126 w.println("};"); 1127 } 1128 1129 1130 1131 /** 1132 * Writes a JAAS configuration file in a form appropriate for IBM VMs. 1133 * 1134 * @param w The writer to use to create the config file. 1135 * @param p The properties to use for GSSAPI authentication. 1136 */ 1137 private static void writeIBMJAASConfig(@NotNull final PrintWriter w, 1138 @NotNull final GSSAPIBindRequestProperties p) 1139 { 1140 // NOTE: It does not appear that the IBM GSSAPI implementation has any 1141 // analog for the renewTGT property, so it will be ignored. 1142 w.println(p.getJAASClientName() + " {"); 1143 w.println(" com.ibm.security.auth.module.Krb5LoginModule required"); 1144 if ((p.getIsInitiator() == null) || p.getIsInitiator().booleanValue()) 1145 { 1146 w.println(" credsType=initiator"); 1147 } 1148 else 1149 { 1150 w.println(" credsType=acceptor"); 1151 } 1152 1153 if (p.refreshKrb5Config()) 1154 { 1155 w.println(" refreshKrb5Config=true"); 1156 } 1157 1158 if (p.useKeyTab()) 1159 { 1160 w.println(" useKeyTab=true"); 1161 if (p.getKeyTabPath() != null) 1162 { 1163 w.println(" keyTab=\"" + p.getKeyTabPath() + '"'); 1164 } 1165 } 1166 1167 if (p.useTicketCache()) 1168 { 1169 final String ticketCachePath = p.getTicketCachePath(); 1170 if (ticketCachePath == null) 1171 { 1172 if (p.requireCachedCredentials()) 1173 { 1174 w.println(" useDefaultCcache=true"); 1175 } 1176 } 1177 else 1178 { 1179 final File f = new File(ticketCachePath); 1180 final String path = f.getAbsolutePath().replace('\\', '/'); 1181 w.println(" useCcache=\"file://" + path + '"'); 1182 } 1183 } 1184 else 1185 { 1186 w.println(" useDefaultCcache=false"); 1187 } 1188 1189 if (p.enableGSSAPIDebugging()) 1190 { 1191 w.println(" debug=true"); 1192 } 1193 1194 w.println(" ;"); 1195 w.println("};"); 1196 } 1197 1198 1199 1200 /** 1201 * Sends this bind request to the target server over the provided connection 1202 * and returns the corresponding response. 1203 * 1204 * @param connection The connection to use to send this bind request to the 1205 * server and read the associated response. 1206 * @param depth The current referral depth for this request. It should 1207 * always be one for the initial request, and should only 1208 * be incremented when following referrals. 1209 * 1210 * @return The bind response read from the server. 1211 * 1212 * @throws LDAPException If a problem occurs while sending the request or 1213 * reading the response. 1214 */ 1215 @Override() 1216 @NotNull() 1217 protected BindResult process(@NotNull final LDAPConnection connection, 1218 final int depth) 1219 throws LDAPException 1220 { 1221 if (! conn.compareAndSet(null, connection)) 1222 { 1223 throw new LDAPException(ResultCode.LOCAL_ERROR, 1224 ERR_GSSAPI_MULTIPLE_CONCURRENT_REQUESTS.get()); 1225 } 1226 1227 setProperty(PROPERTY_CONFIG_FILE, configFilePath); 1228 setProperty(PROPERTY_SUBJECT_CREDS_ONLY, 1229 String.valueOf(useSubjectCredentialsOnly)); 1230 if (Debug.debugEnabled(DebugType.LDAP)) 1231 { 1232 Debug.debug(Level.CONFIG, DebugType.LDAP, 1233 "Using config file property " + PROPERTY_CONFIG_FILE + " = '" + 1234 configFilePath + "'."); 1235 Debug.debug(Level.CONFIG, DebugType.LDAP, 1236 "Using subject creds only property " + PROPERTY_SUBJECT_CREDS_ONLY + 1237 " = '" + useSubjectCredentialsOnly + "'."); 1238 } 1239 1240 if (kdcAddress == null) 1241 { 1242 if (DEFAULT_KDC_ADDRESS == null) 1243 { 1244 clearProperty(PROPERTY_KDC_ADDRESS); 1245 if (Debug.debugEnabled(DebugType.LDAP)) 1246 { 1247 Debug.debug(Level.CONFIG, DebugType.LDAP, 1248 "Clearing kdcAddress property '" + PROPERTY_KDC_ADDRESS + "'."); 1249 } 1250 } 1251 else 1252 { 1253 setProperty(PROPERTY_KDC_ADDRESS, DEFAULT_KDC_ADDRESS); 1254 if (Debug.debugEnabled(DebugType.LDAP)) 1255 { 1256 Debug.debug(Level.CONFIG, DebugType.LDAP, 1257 "Using default kdcAddress property " + PROPERTY_KDC_ADDRESS + 1258 " = '" + DEFAULT_KDC_ADDRESS + "'."); 1259 } 1260 } 1261 } 1262 else 1263 { 1264 setProperty(PROPERTY_KDC_ADDRESS, kdcAddress); 1265 if (Debug.debugEnabled(DebugType.LDAP)) 1266 { 1267 Debug.debug(Level.CONFIG, DebugType.LDAP, 1268 "Using kdcAddress property " + PROPERTY_KDC_ADDRESS + " = '" + 1269 kdcAddress + "'."); 1270 } 1271 } 1272 1273 if (realm == null) 1274 { 1275 if (DEFAULT_REALM == null) 1276 { 1277 clearProperty(PROPERTY_REALM); 1278 if (Debug.debugEnabled(DebugType.LDAP)) 1279 { 1280 Debug.debug(Level.CONFIG, DebugType.LDAP, 1281 "Clearing realm property '" + PROPERTY_REALM + "'."); 1282 } 1283 } 1284 else 1285 { 1286 setProperty(PROPERTY_REALM, DEFAULT_REALM); 1287 if (Debug.debugEnabled(DebugType.LDAP)) 1288 { 1289 Debug.debug(Level.CONFIG, DebugType.LDAP, 1290 "Using default realm property " + PROPERTY_REALM + " = '" + 1291 DEFAULT_REALM + "'."); 1292 } 1293 } 1294 } 1295 else 1296 { 1297 setProperty(PROPERTY_REALM, realm); 1298 if (Debug.debugEnabled(DebugType.LDAP)) 1299 { 1300 Debug.debug(Level.CONFIG, DebugType.LDAP, 1301 "Using realm property " + PROPERTY_REALM + " = '" + realm + "'."); 1302 } 1303 } 1304 1305 try 1306 { 1307 // Reload the configuration before creating the login context, which may 1308 // work around problems that could arise if certain configuration is 1309 // loaded and cached before the above system properties were set. 1310 Configuration.getConfiguration().refresh(); 1311 } 1312 catch (final Exception e) 1313 { 1314 Debug.debugException(e); 1315 } 1316 1317 try 1318 { 1319 final LoginContext context; 1320 try 1321 { 1322 context = new LoginContext(jaasClientName, this); 1323 context.login(); 1324 } 1325 catch (final Exception e) 1326 { 1327 Debug.debugException(e); 1328 1329 throw new LDAPException(ResultCode.LOCAL_ERROR, 1330 ERR_GSSAPI_CANNOT_INITIALIZE_JAAS_CONTEXT.get( 1331 StaticUtils.getExceptionMessage(e)), 1332 e); 1333 } 1334 1335 try 1336 { 1337 return (BindResult) Subject.doAs(context.getSubject(), this); 1338 } 1339 catch (final Exception e) 1340 { 1341 Debug.debugException(e); 1342 if (e instanceof LDAPException) 1343 { 1344 throw (LDAPException) e; 1345 } 1346 else 1347 { 1348 throw new LDAPException(ResultCode.LOCAL_ERROR, 1349 ERR_GSSAPI_AUTHENTICATION_FAILED.get( 1350 StaticUtils.getExceptionMessage(e)), 1351 e); 1352 } 1353 } 1354 } 1355 finally 1356 { 1357 conn.set(null); 1358 } 1359 } 1360 1361 1362 1363 /** 1364 * Perform the privileged portion of the authentication processing. 1365 * 1366 * @return {@code null}, since no return value is actually needed. 1367 * 1368 * @throws LDAPException If a problem occurs during processing. 1369 */ 1370 @InternalUseOnly() 1371 @Override() 1372 @NotNull() 1373 public Object run() 1374 throws LDAPException 1375 { 1376 unhandledCallbackMessages.clear(); 1377 1378 final LDAPConnection connection = conn.get(); 1379 1380 1381 final HashMap<String,Object> saslProperties = 1382 new HashMap<>(StaticUtils.computeMapCapacity(2)); 1383 saslProperties.put(Sasl.QOP, SASLQualityOfProtection.toString(allowedQoP)); 1384 saslProperties.put(Sasl.SERVER_AUTH, "true"); 1385 1386 final SaslClient saslClient; 1387 try 1388 { 1389 String serverName = saslClientServerName; 1390 if (serverName == null) 1391 { 1392 serverName = connection.getConnectedAddress(); 1393 } 1394 1395 final String[] mechanisms = { GSSAPI_MECHANISM_NAME }; 1396 saslClient = Sasl.createSaslClient(mechanisms, authorizationID, 1397 servicePrincipalProtocol, serverName, saslProperties, this); 1398 } 1399 catch (final Exception e) 1400 { 1401 Debug.debugException(e); 1402 throw new LDAPException(ResultCode.LOCAL_ERROR, 1403 ERR_GSSAPI_CANNOT_CREATE_SASL_CLIENT.get( 1404 StaticUtils.getExceptionMessage(e)), 1405 e); 1406 } 1407 1408 final SASLHelper helper = new SASLHelper(this, connection, 1409 GSSAPI_MECHANISM_NAME, saslClient, getControls(), 1410 getResponseTimeoutMillis(connection), unhandledCallbackMessages); 1411 1412 try 1413 { 1414 return helper.processSASLBind(); 1415 } 1416 finally 1417 { 1418 messageID = helper.getMessageID(); 1419 } 1420 } 1421 1422 1423 1424 /** 1425 * {@inheritDoc} 1426 */ 1427 @Override() 1428 @NotNull() 1429 public GSSAPIBindRequest getRebindRequest(@NotNull final String host, 1430 final int port) 1431 { 1432 try 1433 { 1434 final GSSAPIBindRequestProperties gssapiProperties = 1435 new GSSAPIBindRequestProperties(authenticationID, authorizationID, 1436 password, realm, kdcAddress, configFilePath); 1437 gssapiProperties.setAllowedQoP(allowedQoP); 1438 gssapiProperties.setServicePrincipalProtocol(servicePrincipalProtocol); 1439 gssapiProperties.setUseTicketCache(useTicketCache); 1440 gssapiProperties.setRequireCachedCredentials(requireCachedCredentials); 1441 gssapiProperties.setRenewTGT(renewTGT); 1442 gssapiProperties.setUseSubjectCredentialsOnly(useSubjectCredentialsOnly); 1443 gssapiProperties.setTicketCachePath(ticketCachePath); 1444 gssapiProperties.setEnableGSSAPIDebugging(enableGSSAPIDebugging); 1445 gssapiProperties.setJAASClientName(jaasClientName); 1446 gssapiProperties.setSASLClientServerName(saslClientServerName); 1447 gssapiProperties.setSuppressedSystemProperties( 1448 suppressedSystemProperties); 1449 1450 return new GSSAPIBindRequest(gssapiProperties, getControls()); 1451 } 1452 catch (final Exception e) 1453 { 1454 // This should never happen. 1455 Debug.debugException(e); 1456 return null; 1457 } 1458 } 1459 1460 1461 1462 /** 1463 * Handles any necessary callbacks required for SASL authentication. 1464 * 1465 * @param callbacks The set of callbacks to be handled. 1466 * 1467 * @throws UnsupportedCallbackException If an unsupported type of callback 1468 * was received. 1469 */ 1470 @InternalUseOnly() 1471 @Override() 1472 public void handle(@NotNull final Callback[] callbacks) 1473 throws UnsupportedCallbackException 1474 { 1475 for (final Callback callback : callbacks) 1476 { 1477 if (callback instanceof NameCallback) 1478 { 1479 ((NameCallback) callback).setName(authenticationID); 1480 } 1481 else if (callback instanceof PasswordCallback) 1482 { 1483 if (password == null) 1484 { 1485 throw new UnsupportedCallbackException(callback, 1486 ERR_GSSAPI_NO_PASSWORD_AVAILABLE.get()); 1487 } 1488 else 1489 { 1490 ((PasswordCallback) callback).setPassword( 1491 password.stringValue().toCharArray()); 1492 } 1493 } 1494 else if (callback instanceof RealmCallback) 1495 { 1496 final RealmCallback rc = (RealmCallback) callback; 1497 if (realm == null) 1498 { 1499 unhandledCallbackMessages.add( 1500 ERR_GSSAPI_REALM_REQUIRED_BUT_NONE_PROVIDED.get(rc.getPrompt())); 1501 } 1502 else 1503 { 1504 rc.setText(realm); 1505 } 1506 } 1507 else 1508 { 1509 // This is an unexpected callback. 1510 if (Debug.debugEnabled(DebugType.LDAP)) 1511 { 1512 Debug.debug(Level.WARNING, DebugType.LDAP, 1513 "Unexpected GSSAPI SASL callback of type " + 1514 callback.getClass().getName()); 1515 } 1516 1517 unhandledCallbackMessages.add(ERR_GSSAPI_UNEXPECTED_CALLBACK.get( 1518 callback.getClass().getName())); 1519 } 1520 } 1521 } 1522 1523 1524 1525 /** 1526 * {@inheritDoc} 1527 */ 1528 @Override() 1529 public int getLastMessageID() 1530 { 1531 return messageID; 1532 } 1533 1534 1535 1536 /** 1537 * {@inheritDoc} 1538 */ 1539 @Override() 1540 @NotNull() 1541 public GSSAPIBindRequest duplicate() 1542 { 1543 return duplicate(getControls()); 1544 } 1545 1546 1547 1548 /** 1549 * {@inheritDoc} 1550 */ 1551 @Override() 1552 @NotNull() 1553 public GSSAPIBindRequest duplicate(@Nullable final Control[] controls) 1554 { 1555 try 1556 { 1557 final GSSAPIBindRequestProperties gssapiProperties = 1558 new GSSAPIBindRequestProperties(authenticationID, authorizationID, 1559 password, realm, kdcAddress, configFilePath); 1560 gssapiProperties.setAllowedQoP(allowedQoP); 1561 gssapiProperties.setServicePrincipalProtocol(servicePrincipalProtocol); 1562 gssapiProperties.setUseTicketCache(useTicketCache); 1563 gssapiProperties.setRequireCachedCredentials(requireCachedCredentials); 1564 gssapiProperties.setRenewTGT(renewTGT); 1565 gssapiProperties.setRefreshKrb5Config(refreshKrb5Config); 1566 gssapiProperties.setUseKeyTab(useKeyTab); 1567 gssapiProperties.setKeyTabPath(keyTabPath); 1568 gssapiProperties.setUseSubjectCredentialsOnly(useSubjectCredentialsOnly); 1569 gssapiProperties.setTicketCachePath(ticketCachePath); 1570 gssapiProperties.setEnableGSSAPIDebugging(enableGSSAPIDebugging); 1571 gssapiProperties.setJAASClientName(jaasClientName); 1572 gssapiProperties.setSASLClientServerName(saslClientServerName); 1573 gssapiProperties.setIsInitiator(isInitiator); 1574 gssapiProperties.setSuppressedSystemProperties( 1575 suppressedSystemProperties); 1576 1577 final GSSAPIBindRequest bindRequest = 1578 new GSSAPIBindRequest(gssapiProperties, controls); 1579 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 1580 return bindRequest; 1581 } 1582 catch (final Exception e) 1583 { 1584 // This should never happen. 1585 Debug.debugException(e); 1586 return null; 1587 } 1588 } 1589 1590 1591 1592 /** 1593 * Clears the specified system property, unless it is one that is configured 1594 * to be suppressed. 1595 * 1596 * @param name The name of the property to be suppressed. 1597 */ 1598 private void clearProperty(@NotNull final String name) 1599 { 1600 if (! suppressedSystemProperties.contains(name)) 1601 { 1602 StaticUtils.clearSystemProperty(name); 1603 } 1604 } 1605 1606 1607 1608 /** 1609 * Sets the specified system property, unless it is one that is configured to 1610 * be suppressed. 1611 * 1612 * @param name The name of the property to be suppressed. 1613 * @param value The value of the property to be suppressed. 1614 */ 1615 private void setProperty(@NotNull final String name, 1616 @NotNull final String value) 1617 { 1618 if (! suppressedSystemProperties.contains(name)) 1619 { 1620 StaticUtils.setSystemProperty(name, value); 1621 } 1622 } 1623 1624 1625 1626 /** 1627 * {@inheritDoc} 1628 */ 1629 @Override() 1630 public void toString(@NotNull final StringBuilder buffer) 1631 { 1632 buffer.append("GSSAPIBindRequest(authenticationID='"); 1633 buffer.append(authenticationID); 1634 buffer.append('\''); 1635 1636 if (authorizationID != null) 1637 { 1638 buffer.append(", authorizationID='"); 1639 buffer.append(authorizationID); 1640 buffer.append('\''); 1641 } 1642 1643 if (realm != null) 1644 { 1645 buffer.append(", realm='"); 1646 buffer.append(realm); 1647 buffer.append('\''); 1648 } 1649 1650 buffer.append(", qop='"); 1651 buffer.append(SASLQualityOfProtection.toString(allowedQoP)); 1652 buffer.append('\''); 1653 1654 if (kdcAddress != null) 1655 { 1656 buffer.append(", kdcAddress='"); 1657 buffer.append(kdcAddress); 1658 buffer.append('\''); 1659 } 1660 1661 if (isInitiator != null) 1662 { 1663 buffer.append(", isInitiator="); 1664 buffer.append(isInitiator); 1665 } 1666 1667 buffer.append(", jaasClientName='"); 1668 buffer.append(jaasClientName); 1669 buffer.append("', configFilePath='"); 1670 buffer.append(configFilePath); 1671 buffer.append("', servicePrincipalProtocol='"); 1672 buffer.append(servicePrincipalProtocol); 1673 buffer.append("', enableGSSAPIDebugging="); 1674 buffer.append(enableGSSAPIDebugging); 1675 1676 final Control[] controls = getControls(); 1677 if (controls.length > 0) 1678 { 1679 buffer.append(", controls={"); 1680 for (int i=0; i < controls.length; i++) 1681 { 1682 if (i > 0) 1683 { 1684 buffer.append(", "); 1685 } 1686 1687 buffer.append(controls[i]); 1688 } 1689 buffer.append('}'); 1690 } 1691 1692 buffer.append(')'); 1693 } 1694 1695 1696 1697 /** 1698 * {@inheritDoc} 1699 */ 1700 @Override() 1701 public void toCode(@NotNull final List<String> lineList, 1702 @NotNull final String requestID, 1703 final int indentSpaces, final boolean includeProcessing) 1704 { 1705 // Create and update the bind request properties object. 1706 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 1707 "GSSAPIBindRequestProperties", requestID + "RequestProperties", 1708 "new GSSAPIBindRequestProperties", 1709 ToCodeArgHelper.createString(authenticationID, "Authentication ID"), 1710 ToCodeArgHelper.createString("---redacted-password---", "Password")); 1711 1712 if (authorizationID != null) 1713 { 1714 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1715 requestID + "RequestProperties.setAuthorizationID", 1716 ToCodeArgHelper.createString(authorizationID, null)); 1717 } 1718 1719 if (realm != null) 1720 { 1721 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1722 requestID + "RequestProperties.setRealm", 1723 ToCodeArgHelper.createString(realm, null)); 1724 } 1725 1726 final ArrayList<String> qopValues = new ArrayList<>(3); 1727 for (final SASLQualityOfProtection qop : allowedQoP) 1728 { 1729 qopValues.add("SASLQualityOfProtection." + qop.name()); 1730 } 1731 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1732 requestID + "RequestProperties.setAllowedQoP", 1733 ToCodeArgHelper.createRaw(qopValues, null)); 1734 1735 if (kdcAddress != null) 1736 { 1737 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1738 requestID + "RequestProperties.setKDCAddress", 1739 ToCodeArgHelper.createString(kdcAddress, null)); 1740 } 1741 1742 if (jaasClientName != null) 1743 { 1744 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1745 requestID + "RequestProperties.setJAASClientName", 1746 ToCodeArgHelper.createString(jaasClientName, null)); 1747 } 1748 1749 if (configFilePath != null) 1750 { 1751 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1752 requestID + "RequestProperties.setConfigFilePath", 1753 ToCodeArgHelper.createString(configFilePath, null)); 1754 } 1755 1756 if (saslClientServerName != null) 1757 { 1758 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1759 requestID + "RequestProperties.setSASLClientServerName", 1760 ToCodeArgHelper.createString(saslClientServerName, null)); 1761 } 1762 1763 if (servicePrincipalProtocol != null) 1764 { 1765 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1766 requestID + "RequestProperties.setServicePrincipalProtocol", 1767 ToCodeArgHelper.createString(servicePrincipalProtocol, null)); 1768 } 1769 1770 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1771 requestID + "RequestProperties.setRefreshKrb5Config", 1772 ToCodeArgHelper.createBoolean(refreshKrb5Config, null)); 1773 1774 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1775 requestID + "RequestProperties.setUseKeyTab", 1776 ToCodeArgHelper.createBoolean(useKeyTab, null)); 1777 1778 if (keyTabPath != null) 1779 { 1780 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1781 requestID + "RequestProperties.setKeyTabPath", 1782 ToCodeArgHelper.createString(keyTabPath, null)); 1783 } 1784 1785 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1786 requestID + "RequestProperties.setUseSubjectCredentialsOnly", 1787 ToCodeArgHelper.createBoolean(useSubjectCredentialsOnly, null)); 1788 1789 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1790 requestID + "RequestProperties.setUseTicketCache", 1791 ToCodeArgHelper.createBoolean(useTicketCache, null)); 1792 1793 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1794 requestID + "RequestProperties.setRequireCachedCredentials", 1795 ToCodeArgHelper.createBoolean(requireCachedCredentials, null)); 1796 1797 if (ticketCachePath != null) 1798 { 1799 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1800 requestID + "RequestProperties.setTicketCachePath", 1801 ToCodeArgHelper.createString(ticketCachePath, null)); 1802 } 1803 1804 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1805 requestID + "RequestProperties.setRenewTGT", 1806 ToCodeArgHelper.createBoolean(renewTGT, null)); 1807 1808 if (isInitiator != null) 1809 { 1810 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1811 requestID + "RequestProperties.setIsInitiator", 1812 ToCodeArgHelper.createBoolean(isInitiator, null)); 1813 } 1814 1815 if ((suppressedSystemProperties != null) && 1816 (! suppressedSystemProperties.isEmpty())) 1817 { 1818 final ArrayList<ToCodeArgHelper> suppressedArgs = 1819 new ArrayList<>(suppressedSystemProperties.size()); 1820 for (final String s : suppressedSystemProperties) 1821 { 1822 suppressedArgs.add(ToCodeArgHelper.createString(s, null)); 1823 } 1824 1825 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "List<String>", 1826 requestID + "SuppressedProperties", "Arrays.asList", suppressedArgs); 1827 1828 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1829 requestID + "RequestProperties.setSuppressedSystemProperties", 1830 ToCodeArgHelper.createRaw(requestID + "SuppressedProperties", null)); 1831 } 1832 1833 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1834 requestID + "RequestProperties.setEnableGSSAPIDebugging", 1835 ToCodeArgHelper.createBoolean(enableGSSAPIDebugging, null)); 1836 1837 1838 // Create the request variable. 1839 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(2); 1840 constructorArgs.add( 1841 ToCodeArgHelper.createRaw(requestID + "RequestProperties", null)); 1842 1843 final Control[] controls = getControls(); 1844 if (controls.length > 0) 1845 { 1846 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 1847 "Bind Controls")); 1848 } 1849 1850 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "GSSAPIBindRequest", 1851 requestID + "Request", "new GSSAPIBindRequest", constructorArgs); 1852 1853 1854 // Add lines for processing the request and obtaining the result. 1855 if (includeProcessing) 1856 { 1857 // Generate a string with the appropriate indent. 1858 final StringBuilder buffer = new StringBuilder(); 1859 for (int i=0; i < indentSpaces; i++) 1860 { 1861 buffer.append(' '); 1862 } 1863 final String indent = buffer.toString(); 1864 1865 lineList.add(""); 1866 lineList.add(indent + "try"); 1867 lineList.add(indent + '{'); 1868 lineList.add(indent + " BindResult " + requestID + 1869 "Result = connection.bind(" + requestID + "Request);"); 1870 lineList.add(indent + " // The bind was processed successfully."); 1871 lineList.add(indent + '}'); 1872 lineList.add(indent + "catch (LDAPException e)"); 1873 lineList.add(indent + '{'); 1874 lineList.add(indent + " // The bind failed. Maybe the following will " + 1875 "help explain why."); 1876 lineList.add(indent + " // Note that the connection is now likely in " + 1877 "an unauthenticated state."); 1878 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 1879 lineList.add(indent + " String message = e.getMessage();"); 1880 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 1881 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 1882 lineList.add(indent + " Control[] responseControls = " + 1883 "e.getResponseControls();"); 1884 lineList.add(indent + '}'); 1885 } 1886 } 1887}