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 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.io.File;
026 import java.io.FileWriter;
027 import java.io.PrintWriter;
028 import java.security.PrivilegedExceptionAction;
029 import java.util.ArrayList;
030 import java.util.HashMap;
031 import java.util.List;
032 import java.util.Set;
033 import java.util.concurrent.atomic.AtomicReference;
034 import java.util.logging.Level;
035 import javax.security.auth.Subject;
036 import javax.security.auth.callback.Callback;
037 import javax.security.auth.callback.CallbackHandler;
038 import javax.security.auth.callback.NameCallback;
039 import javax.security.auth.callback.PasswordCallback;
040 import javax.security.auth.callback.UnsupportedCallbackException;
041 import javax.security.auth.login.LoginContext;
042 import javax.security.sasl.RealmCallback;
043 import javax.security.sasl.Sasl;
044 import javax.security.sasl.SaslClient;
045
046 import com.unboundid.asn1.ASN1OctetString;
047 import com.unboundid.util.DebugType;
048 import com.unboundid.util.InternalUseOnly;
049 import com.unboundid.util.NotMutable;
050 import com.unboundid.util.ThreadSafety;
051 import com.unboundid.util.ThreadSafetyLevel;
052
053 import static com.unboundid.ldap.sdk.LDAPMessages.*;
054 import static com.unboundid.util.Debug.*;
055 import static com.unboundid.util.StaticUtils.*;
056 import 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)
125 public 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 }