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