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