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