001/*
002 * Copyright 2011-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2011-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2011-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk;
037
038
039
040import java.io.Serializable;
041import java.util.ArrayList;
042import java.util.Collection;
043import java.util.Collections;
044import java.util.Iterator;
045import java.util.LinkedHashSet;
046import java.util.List;
047import java.util.Set;
048
049import com.unboundid.asn1.ASN1OctetString;
050import com.unboundid.util.CryptoHelper;
051import com.unboundid.util.Debug;
052import com.unboundid.util.Mutable;
053import com.unboundid.util.NotNull;
054import com.unboundid.util.Nullable;
055import com.unboundid.util.StaticUtils;
056import com.unboundid.util.ThreadSafety;
057import com.unboundid.util.ThreadSafetyLevel;
058import com.unboundid.util.Validator;
059import com.unboundid.util.json.JSONBuffer;
060
061import static com.unboundid.ldap.sdk.LDAPMessages.*;
062
063
064
065/**
066 * This class provides a data structure that may be used to hold a number of
067 * properties that may be used during processing for a SASL GSSAPI bind
068 * operation.
069 */
070@Mutable()
071@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
072public final class GSSAPIBindRequestProperties
073       implements Serializable
074{
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = 6872295509330315713L;
079
080
081
082  // The password for the GSSAPI bind request.
083  @Nullable private ASN1OctetString password;
084
085  // Indicates whether to enable JVM-level debugging for GSSAPI processing.
086  private boolean enableGSSAPIDebugging;
087
088  // Indicates whether the client should be considered the GSSAPI initiator or
089  // the acceptor.
090  @Nullable private Boolean isInitiator;
091
092  // Indicates whether to attempt to refresh the configuration before the JAAS
093  // login method is called.
094  private boolean refreshKrb5Config;
095
096  // Indicates whether to attempt to renew the client's existing ticket-granting
097  // ticket if authentication uses an existing Kerberos session.
098  private boolean renewTGT;
099
100  // Indicates whether to require that the credentials be obtained from the
101  // ticket cache such that authentication will fail if the client does not have
102  // an existing Kerberos session.
103  private boolean requireCachedCredentials;
104
105  // Indicates whether to allow the to obtain the credentials to be obtained
106  // from a keytab.
107  private boolean useKeyTab;
108
109  // Indicates whether to allow the client to use credentials that are outside
110  // of the current subject.
111  private boolean useSubjectCredentialsOnly;
112
113  // Indicates whether to enable the use of a ticket cache.
114  private boolean useTicketCache;
115
116  // A digest of the settings that are relevant to a JAAS configuration file.
117  @Nullable private byte[] configFileDigest;
118
119  // The type of channel binding to use for the bind request.
120  @NotNull private GSSAPIChannelBindingType channelBindingType;
121
122  // The SASL quality of protection value(s) allowed for the GSSAPI bind
123  // request.
124  @NotNull private List<SASLQualityOfProtection> allowedQoP;
125
126  // The names of any system properties that should not be altered by GSSAPI
127  // processing.
128  @NotNull private Set<String> suppressedSystemProperties;
129
130  // The authentication ID string for the GSSAPI bind request.
131  @Nullable private String authenticationID;
132
133  // The authorization ID string for the GSSAPI bind request, if available.
134  @Nullable private String authorizationID;
135
136  // The path to the JAAS configuration file to use for bind processing.
137  @Nullable private String configFilePath;
138
139  // The name that will be used to identify this client in the JAAS framework.
140  @NotNull private String jaasClientName;
141
142  // The KDC address for the GSSAPI bind request, if available.
143  @Nullable private String kdcAddress;
144
145  // The path to the keytab file to use if useKeyTab is true.
146  @Nullable private String keyTabPath;
147
148  // The realm for the GSSAPI bind request, if available.
149  @Nullable private String realm;
150
151  // The server name to use when creating the SASL client.
152  @Nullable private String saslClientServerName;
153
154  // The protocol that should be used in the Kerberos service principal for
155  // the server system.
156  @NotNull private String servicePrincipalProtocol;
157
158  // The path to the Kerberos ticket cache to use.
159  @Nullable private String ticketCachePath;
160
161
162
163  /**
164   * Creates a new set of GSSAPI bind request properties with the provided
165   * information.
166   *
167   * @param  authenticationID  The authentication ID for the GSSAPI bind
168   *                           request.  It may be {@code null} if an existing
169   *                           Kerberos session should be used.
170   * @param  password          The password for the GSSAPI bind request.  It may
171   *                           be {@code null} if an existing Kerberos session
172   *                           should be used.
173   */
174  public GSSAPIBindRequestProperties(@Nullable final String authenticationID,
175                                     @Nullable final String password)
176  {
177    this(authenticationID, null,
178         (password == null ? null : new ASN1OctetString(password)), null, null,
179         null);
180  }
181
182
183
184  /**
185   * Creates a new set of GSSAPI bind request properties with the provided
186   * information.
187   *
188   * @param  authenticationID  The authentication ID for the GSSAPI bind
189   *                           request.  It may be {@code null} if an existing
190   *                           Kerberos session should be used.
191   * @param  password          The password for the GSSAPI bind request.  It may
192   *                           be {@code null} if an existing Kerberos session
193   *                           should be used.
194   */
195  public GSSAPIBindRequestProperties(@Nullable final String authenticationID,
196                                     @Nullable final byte[] password)
197  {
198    this(authenticationID, null,
199         (password == null ? null : new ASN1OctetString(password)), null, null,
200         null);
201  }
202
203
204
205  /**
206   * Creates a new set of GSSAPI bind request properties with the provided
207   * information.
208   *
209   * @param  authenticationID  The authentication ID for the GSSAPI bind
210   *                           request.  It may be {@code null} if an existing
211   *                           Kerberos session should be used.
212   * @param  authorizationID   The authorization ID for the GSSAPI bind request.
213   *                           It may be {@code null} if the authorization ID
214   *                           should be the same as the authentication ID.
215   * @param  password          The password for the GSSAPI bind request.  It may
216   *                           be {@code null} if an existing Kerberos session
217   *                           should be used.
218   * @param  realm             The realm to use for the authentication.  It may
219   *                           be {@code null} to attempt to use the default
220   *                           realm from the system configuration.
221   * @param  kdcAddress        The address of the Kerberos key distribution
222   *                           center.  It may be {@code null} to attempt to use
223   *                           the default KDC from the system configuration.
224   * @param  configFilePath    The path to the JAAS configuration file to use
225   *                           for the authentication processing.  It may be
226   *                           {@code null} to use the default JAAS
227   *                           configuration.
228   */
229  GSSAPIBindRequestProperties(@Nullable final String authenticationID,
230                              @Nullable final String authorizationID,
231                              @Nullable final ASN1OctetString password,
232                              @Nullable final String realm,
233                              @Nullable final String kdcAddress,
234                              @Nullable final String configFilePath)
235  {
236    this.authenticationID = authenticationID;
237    this.authorizationID  = authorizationID;
238    this.password         = password;
239    this.realm            = realm;
240    this.kdcAddress       = kdcAddress;
241    this.configFilePath   = configFilePath;
242
243    servicePrincipalProtocol   = "ldap";
244    enableGSSAPIDebugging      = false;
245    jaasClientName             = "GSSAPIBindRequest";
246    isInitiator                = null;
247    refreshKrb5Config          = false;
248    renewTGT                   = false;
249    useKeyTab                  = false;
250    useSubjectCredentialsOnly  = true;
251    useTicketCache             = true;
252    requireCachedCredentials   = false;
253    saslClientServerName       = null;
254    keyTabPath                 = null;
255    ticketCachePath            = null;
256    suppressedSystemProperties = Collections.emptySet();
257    allowedQoP                 =
258         Collections.singletonList(SASLQualityOfProtection.AUTH);
259    channelBindingType         = GSSAPIChannelBindingType.NONE;
260  }
261
262
263
264  /**
265   * Retrieves the authentication ID for the GSSAPI bind request, if defined.
266   *
267   * @return  The authentication ID for the GSSAPI bind request, or {@code null}
268   *          if an existing Kerberos session should be used.
269   */
270  @Nullable()
271  public String getAuthenticationID()
272  {
273    return authenticationID;
274  }
275
276
277
278  /**
279   * Sets the authentication ID for the GSSAPI bind request.
280   *
281   * @param  authenticationID  The authentication ID for the GSSAPI bind
282   *                           request.  It may be {@code null} if an existing
283   *                           Kerberos session should be used.
284   */
285  public void setAuthenticationID(@Nullable final String authenticationID)
286  {
287    this.authenticationID = authenticationID;
288  }
289
290
291
292  /**
293   * Retrieves the authorization ID for the GSSAPI bind request, if defined.
294   *
295   * @return  The authorizationID for the GSSAPI bind request, or {@code null}
296   *          if the authorization ID should be the same as the authentication
297   *          ID.
298   */
299  @Nullable()
300  public String getAuthorizationID()
301  {
302    return authorizationID;
303  }
304
305
306
307  /**
308   * Specifies the authorization ID for the GSSAPI bind request.
309   *
310   * @param  authorizationID  The authorization ID for the GSSAPI bind request.
311   *                          It may be {@code null} if the authorization ID
312   *                          should be the same as the authentication ID.
313   */
314  public void setAuthorizationID(@Nullable final String authorizationID)
315  {
316    this.authorizationID = authorizationID;
317  }
318
319
320
321  /**
322   * Retrieves the password that should be used for the GSSAPI bind request, if
323   * defined.
324   *
325   * @return  The password that should be used for the GSSAPI bind request, or
326   *          {@code null} if an existing Kerberos session should be used.
327   */
328  @Nullable()
329  public ASN1OctetString getPassword()
330  {
331    return password;
332  }
333
334
335
336  /**
337   * Specifies the password that should be used for the GSSAPI bind request.
338   *
339   * @param  password  The password that should be used for the GSSAPI bind
340   *                   request.  It may be {@code null} if an existing
341   *                   Kerberos session should be used.
342   */
343  public void setPassword(@Nullable final String password)
344  {
345    if (password == null)
346    {
347      this.password = null;
348    }
349    else
350    {
351      this.password = new ASN1OctetString(password);
352    }
353  }
354
355
356
357  /**
358   * Specifies the password that should be used for the GSSAPI bind request.
359   *
360   * @param  password  The password that should be used for the GSSAPI bind
361   *                   request.  It may be {@code null} if an existing
362   *                   Kerberos session should be used.
363   */
364  public void setPassword(@Nullable final byte[] password)
365  {
366    if (password == null)
367    {
368      this.password = null;
369    }
370    else
371    {
372      this.password = new ASN1OctetString(password);
373    }
374  }
375
376
377
378  /**
379   * Specifies the password that should be used for the GSSAPI bind request.
380   *
381   * @param  password  The password that should be used for the GSSAPI bind
382   *                   request.  It may be {@code null} if an existing
383   *                   Kerberos session should be used.
384   */
385  public void setPassword(@Nullable final ASN1OctetString password)
386  {
387    this.password = password;
388  }
389
390
391
392  /**
393   * Retrieves the realm to use for the GSSAPI bind request, if defined.
394   *
395   * @return  The realm to use for the GSSAPI bind request, or {@code null} if
396   *          the request should attempt to use the default realm from the
397   *          system configuration.
398   */
399  @Nullable()
400  public String getRealm()
401  {
402    return realm;
403  }
404
405
406
407  /**
408   * Specifies the realm to use for the GSSAPI bind request.
409   *
410   * @param  realm  The realm to use for the GSSAPI bind request.  It may be
411   *                {@code null} if the request should attempt to use the
412   *                default realm from the system configuration.
413   */
414  public void setRealm(@Nullable final String realm)
415  {
416    this.realm = realm;
417  }
418
419
420
421  /**
422   * Retrieves the list of allowed qualities of protection that may be used for
423   * communication that occurs on the connection after the authentication has
424   * completed, in order from most preferred to least preferred.
425   *
426   * @return  The list of allowed qualities of protection that may be used for
427   *          communication that occurs on the connection after the
428   *          authentication has completed, in order from most preferred to
429   *          least preferred.
430   */
431  @NotNull()
432  public List<SASLQualityOfProtection> getAllowedQoP()
433  {
434    return allowedQoP;
435  }
436
437
438
439  /**
440   * Specifies the list of allowed qualities of protection that may be used for
441   * communication that occurs on the connection after the authentication has
442   * completed, in order from most preferred to least preferred.
443   *
444   * @param  allowedQoP  The list of allowed qualities of protection that may be
445   *                     used for communication that occurs on the connection
446   *                     after the authentication has completed, in order from
447   *                     most preferred to least preferred.  If this is
448   *                     {@code null} or empty, then a list containing only the
449   *                     {@link SASLQualityOfProtection#AUTH} quality of
450   *                     protection value will be used.
451   */
452  public void setAllowedQoP(
453                   @Nullable final List<SASLQualityOfProtection> allowedQoP)
454  {
455    if ((allowedQoP == null) || allowedQoP.isEmpty())
456    {
457      this.allowedQoP = Collections.singletonList(SASLQualityOfProtection.AUTH);
458    }
459    else
460    {
461      this.allowedQoP =
462           Collections.unmodifiableList(new ArrayList<>(allowedQoP));
463    }
464  }
465
466
467
468  /**
469   * Specifies the list of allowed qualities of protection that may be used for
470   * communication that occurs on the connection after the authentication has
471   * completed, in order from most preferred to least preferred.
472   *
473   * @param  allowedQoP  The list of allowed qualities of protection that may be
474   *                     used for communication that occurs on the connection
475   *                     after the authentication has completed, in order from
476   *                     most preferred to least preferred.  If this is
477   *                     {@code null} or empty, then a list containing only the
478   *                     {@link SASLQualityOfProtection#AUTH} quality of
479   *                     protection value will be used.
480   */
481  public void setAllowedQoP(
482                   @Nullable final SASLQualityOfProtection... allowedQoP)
483  {
484    setAllowedQoP(StaticUtils.toList(allowedQoP));
485  }
486
487
488
489  /**
490   * Retrieves the address to use for the Kerberos key distribution center,
491   * if defined.
492   *
493   * @return  The address to use for the Kerberos key distribution center, or
494   *          {@code null} if request should attempt to determine the KDC
495   *          address from the system configuration.
496   */
497  @Nullable()
498  public String getKDCAddress()
499  {
500    return kdcAddress;
501  }
502
503
504
505  /**
506   * Specifies the address to use for the Kerberos key distribution center.
507   *
508   * @param  kdcAddress  The address to use for the Kerberos key distribution
509   *                     center.  It may be {@code null} if the request should
510   *                     attempt to determine the KDC address from the system
511   *                     configuration.
512   */
513  public void setKDCAddress(@Nullable final String kdcAddress)
514  {
515    this.kdcAddress = kdcAddress;
516  }
517
518
519
520  /**
521   * Retrieves the name that will be used to identify this client in the JAAS
522   * framework.
523   *
524   * @return  The name that will be used to identify this client in the JAAS
525   *          framework.
526   */
527  @NotNull()
528  public String getJAASClientName()
529  {
530    return jaasClientName;
531  }
532
533
534
535  /**
536   * Specifies the name that will be used to identify this client in the JAAS
537   * framework.
538   *
539   * @param  jaasClientName  The name that will be used to identify this client
540   *                         in the JAAS framework.  It must not be
541   *                         {@code null} or empty.
542   */
543  public void setJAASClientName(@NotNull final String jaasClientName)
544  {
545    Validator.ensureNotNull(jaasClientName);
546
547    this.jaasClientName = jaasClientName;
548    configFileDigest = null;
549  }
550
551
552
553  /**
554   * Retrieves the path to a JAAS configuration file that should be used when
555   * processing the GSSAPI bind request, if defined.
556   *
557   * @return  The path to a JAAS configuration file that should be used when
558   *          processing the GSSAPI bind request, or {@code null} if a JAAS
559   *          configuration file should be automatically constructed for the
560   *          bind request.
561   */
562  @Nullable()
563  public String getConfigFilePath()
564  {
565    return configFilePath;
566  }
567
568
569
570  /**
571   * Specifies the path to a JAAS configuration file that should be used when
572   * processing the GSSAPI bind request.
573   *
574   * @param  configFilePath  The path to a JAAS configuration file that should
575   *                         be used when processing the GSSAPI bind request.
576   *                         It may be {@code null} if a configuration file
577   *                         should be automatically constructed for the bind
578   *                         request.
579   */
580  public void setConfigFilePath(@Nullable final String configFilePath)
581  {
582    this.configFilePath = configFilePath;
583  }
584
585
586
587  /**
588   * Retrieves the server name that should be used when creating the Java
589   * {@code SaslClient}, if one is defined.
590   *
591   * @return  The server name that should be used when creating the Java
592   *          {@code SaslClient}, or {@code null} if none is defined and the
593   *          {@code SaslClient} should use the address specified when
594   *          establishing the connection.
595   */
596  @Nullable()
597  public String getSASLClientServerName()
598  {
599    return saslClientServerName;
600  }
601
602
603
604  /**
605   * Specifies the server name that should be used when creating the Java
606   * {@code SaslClient}.
607   *
608   * @param  saslClientServerName  The server name that should be used when
609   *                               creating the Java {@code SaslClient}.  It may
610   *                               be {@code null} to indicate that the
611   *                               {@code SaslClient} should use the address
612   *                               specified when establishing the connection.
613   */
614  public void setSASLClientServerName(
615                   @Nullable final String saslClientServerName)
616  {
617    this.saslClientServerName = saslClientServerName;
618  }
619
620
621
622  /**
623   * Retrieves the protocol specified in the service principal that the
624   * directory server uses for its communication with the KDC.  The service
625   * principal is usually something like "ldap/directory.example.com", where
626   * "ldap" is the protocol and "directory.example.com" is the fully-qualified
627   * address of the directory server system, but some servers may allow
628   * authentication with a service principal with a protocol other than "ldap".
629   *
630   * @return  The protocol specified in the service principal that the directory
631   *          server uses for its communication with the KDC.
632   */
633  @NotNull()
634  public String getServicePrincipalProtocol()
635  {
636    return servicePrincipalProtocol;
637  }
638
639
640
641  /**
642   * Specifies the protocol specified in the service principal that the
643   * directory server uses for its communication with the KDC.  This should
644   * generally be "ldap", but some servers may allow a service principal with a
645   * protocol other than "ldap".
646   *
647   * @param  servicePrincipalProtocol  The protocol specified in the service
648   *                                   principal that the directory server uses
649   *                                   for its communication with the KDC.
650   */
651  public void setServicePrincipalProtocol(
652                   @NotNull final String servicePrincipalProtocol)
653  {
654    Validator.ensureNotNull(servicePrincipalProtocol);
655
656    this.servicePrincipalProtocol = servicePrincipalProtocol;
657  }
658
659
660
661  /**
662   * Indicates whether to refresh the configuration before the JAAS
663   * {@code login} method is called.
664   *
665   * @return  {@code true} if the GSSAPI implementation should refresh the
666   *          configuration before the JAAS {@code login} method is called, or
667   *          {@code false} if not.
668   */
669  public boolean refreshKrb5Config()
670  {
671    return refreshKrb5Config;
672  }
673
674
675
676  /**
677   * Specifies whether to refresh the configuration before the JAAS
678   * {@code login} method is called.
679   *
680   * @param  refreshKrb5Config  Indicates whether to refresh the configuration
681   *                            before the JAAS {@code login} method is called.
682   */
683  public void setRefreshKrb5Config(final boolean refreshKrb5Config)
684  {
685    this.refreshKrb5Config = refreshKrb5Config;
686    configFileDigest = null;
687  }
688
689
690
691  /**
692   * Indicates whether to allow the client to use credentials that are outside
693   * of the current subject, obtained via some system-specific mechanism.
694   *
695   * @return  {@code true} if the client will only be allowed to use credentials
696   *          that are within the current subject, or {@code false} if the
697   *          client will be allowed to use credentials outside the current
698   *          subject.
699   */
700  public boolean useSubjectCredentialsOnly()
701  {
702    return useSubjectCredentialsOnly;
703  }
704
705
706
707  /**
708   * Specifies whether to allow the client to use credentials that are outside
709   * the current subject.  If this is {@code false}, then a system-specific
710   * mechanism may be used in an attempt to obtain credentials from an
711   * existing session.
712   *
713   * @param  useSubjectCredentialsOnly  Indicates whether to allow the client to
714   *                                    use credentials that are outside of the
715   *                                    current subject.
716   */
717  public void setUseSubjectCredentialsOnly(
718                   final boolean useSubjectCredentialsOnly)
719  {
720    this.useSubjectCredentialsOnly = useSubjectCredentialsOnly;
721  }
722
723
724
725  /**
726   * Indicates whether to use a keytab to obtain the user credentials.
727   *
728   * @return  {@code true} if the GSSAPI login attempt should use a keytab to
729   *          obtain the user credentials, or {@code false} if not.
730   */
731  public boolean useKeyTab()
732  {
733    return useKeyTab;
734  }
735
736
737
738  /**
739   * Specifies whether to use a keytab to obtain the user credentials.
740   *
741   * @param  useKeyTab  Indicates whether to use a keytab to obtain the user
742   *                    credentials.
743   */
744  public void setUseKeyTab(final boolean useKeyTab)
745  {
746    this.useKeyTab = useKeyTab;
747    configFileDigest = null;
748  }
749
750
751
752  /**
753   * Retrieves the path to the keytab file from which to obtain the user
754   * credentials.  This will only be used if {@link #useKeyTab} returns
755   * {@code true}.
756   *
757   * @return  The path to the keytab file from which to obtain the user
758   *          credentials, or {@code null} if the default keytab location should
759   *          be used.
760   */
761  @Nullable()
762  public String getKeyTabPath()
763  {
764    return keyTabPath;
765  }
766
767
768
769  /**
770   * Specifies the path to the keytab file from which to obtain the user
771   * credentials.
772   *
773   * @param  keyTabPath  The path to the keytab file from which to obtain the
774   *                     user credentials.  It may be {@code null} if the
775   *                     default keytab location should be used.
776   */
777  public void setKeyTabPath(@Nullable final String keyTabPath)
778  {
779    this.keyTabPath = keyTabPath;
780    configFileDigest = null;
781  }
782
783
784
785  /**
786   * Indicates whether to enable the use of a ticket cache to to avoid the need
787   * to supply credentials if the client already has an existing Kerberos
788   * session.
789   *
790   * @return  {@code true} if a ticket cache may be used to take advantage of an
791   *          existing Kerberos session, or {@code false} if Kerberos
792   *          credentials should always be provided.
793   */
794  public boolean useTicketCache()
795  {
796    return useTicketCache;
797  }
798
799
800
801  /**
802   * Specifies whether to enable the use of a ticket cache to to avoid the need
803   * to supply credentials if the client already has an existing Kerberos
804   * session.
805   *
806   * @param  useTicketCache  Indicates whether to enable the use of a ticket
807   *                         cache to to avoid the need to supply credentials if
808   *                         the client already has an existing Kerberos
809   *                         session.
810   */
811  public void setUseTicketCache(final boolean useTicketCache)
812  {
813    this.useTicketCache = useTicketCache;
814    configFileDigest = null;
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   * Specifies whether an GSSAPI authentication should only occur using an
837   * existing Kerberos session.
838   *
839   * @param  requireCachedCredentials  Indicates whether an existing Kerberos
840   *                                   session will be required for
841   *                                   authentication.  If {@code true}, then
842   *                                   authentication will fail if the client
843   *                                   does not already have an existing
844   *                                   Kerberos session.  This will be ignored
845   *                                   if {@code useTicketCache} is false.
846   */
847  public void setRequireCachedCredentials(
848                   final boolean requireCachedCredentials)
849  {
850    this.requireCachedCredentials = requireCachedCredentials;
851    configFileDigest = null;
852  }
853
854
855
856  /**
857   * Retrieves the path to the Kerberos ticket cache file that should be used
858   * during authentication, if defined.
859   *
860   * @return  The path to the Kerberos ticket cache file that should be used
861   *          during authentication, or {@code null} if the default ticket cache
862   *          file should be used.
863   */
864  @Nullable()
865  public String getTicketCachePath()
866  {
867    return ticketCachePath;
868  }
869
870
871
872  /**
873   * Specifies the path to the Kerberos ticket cache file that should be used
874   * during authentication.
875   *
876   * @param  ticketCachePath  The path to the Kerberos ticket cache file that
877   *                          should be used during authentication.  It may be
878   *                          {@code null} if the default ticket cache file
879   *                          should be used.
880   */
881  public void setTicketCachePath(@Nullable final String ticketCachePath)
882  {
883    this.ticketCachePath = ticketCachePath;
884    configFileDigest = null;
885  }
886
887
888
889  /**
890   * Indicates whether to attempt to renew the client's ticket-granting ticket
891   * (TGT) if an existing Kerberos session is used to authenticate.
892   *
893   * @return  {@code true} if the client should attempt to renew its
894   *          ticket-granting ticket if the authentication is processed using an
895   *          existing Kerberos session, or {@code false} if not.
896   */
897  public boolean renewTGT()
898  {
899    return renewTGT;
900  }
901
902
903
904  /**
905   * Specifies whether to attempt to renew the client's ticket-granting ticket
906   * (TGT) if an existing Kerberos session is used to authenticate.
907   *
908   * @param  renewTGT  Indicates whether to attempt to renew the client's
909   *                   ticket-granting ticket if an existing Kerberos session is
910   *                   used to authenticate.
911   */
912  public void setRenewTGT(final boolean renewTGT)
913  {
914    this.renewTGT = renewTGT;
915    configFileDigest = null;
916  }
917
918
919
920  /**
921   * Indicates whether the client should be configured so that it explicitly
922   * indicates whether it is the initiator or the acceptor.
923   *
924   * @return  {@code Boolean.TRUE} if the client should explicitly indicate that
925   *          it is the GSSAPI initiator, {@code Boolean.FALSE} if the client
926   *          should explicitly indicate that it is the GSSAPI acceptor, or
927   *          {@code null} if the client should not explicitly indicate either
928   *          state (which is the default if the {@link #setIsInitiator}  method
929   *          has not been called).
930   */
931  @Nullable()
932  public Boolean getIsInitiator()
933  {
934    return isInitiator;
935  }
936
937
938
939  /**
940   * Specifies whether the client should explicitly indicate whether it is the
941   * GSSAPI initiator or acceptor.
942   *
943   * @param  isInitiator  Indicates whether the client should be considered the
944   *                      GSSAPI initiator.  A value of {@code Boolean.TRUE}
945   *                      means the client should explicitly indicate that it is
946   *                      the GSSAPI initiator.  A value of
947   *                      {@code Boolean.FALSE} means the client should
948   *                      explicitly indicate that it is the GSSAPI acceptor.  A
949   *                      value of  {@code null} means that the client will not
950   *                      explicitly indicate one way or the other (although
951   *                      this behavior will only apply to Sun/Oracle-based
952   *                      implementations; on the IBM implementation, the client
953   *                      will always be the initiator unless explicitly
954   *                      configured otherwise).
955   */
956  public void setIsInitiator(@Nullable final Boolean isInitiator)
957  {
958    this.isInitiator = isInitiator;
959    configFileDigest = null;
960  }
961
962
963
964  /**
965   * Retrieves the type of channel binding that should be used for the GSSAPI
966   * bind request.
967   *
968   * @return  The type of channel binding that should be used for the GSSAPI
969   *          bind request.
970   */
971  @NotNull()
972  public GSSAPIChannelBindingType getChannelBindingType()
973  {
974    return channelBindingType;
975  }
976
977
978
979  /**
980   * Specifies the type of channel binding that should be used for the GSSAPI
981   * bind request.  Note that channel binding support is dependent upon the
982   * underlying JVM and may not be available in all cases.
983   *
984   * @param  channelBindingType  The type of channel binding that should be used
985   *                             for the GSSAPI bind request.  It may be
986   *                             {@code null} or {@code NONE} if no channel
987   *                             binding should be used.
988   */
989  public void setChannelBindingType(
990       @Nullable final GSSAPIChannelBindingType channelBindingType)
991  {
992    if (channelBindingType == null)
993    {
994      this.channelBindingType = GSSAPIChannelBindingType.NONE;
995    }
996    else
997    {
998      this.channelBindingType = channelBindingType;
999    }
1000  }
1001
1002
1003
1004  /**
1005   * Retrieves a set of system properties that will not be altered by GSSAPI
1006   * processing.
1007   *
1008   * @return  A set of system properties that will not be altered by GSSAPI
1009   *          processing.
1010   */
1011  @NotNull()
1012  public Set<String> getSuppressedSystemProperties()
1013  {
1014    return suppressedSystemProperties;
1015  }
1016
1017
1018
1019  /**
1020   * Specifies a set of system properties that will not be altered by GSSAPI
1021   * processing.  This should generally only be used in cases in which the
1022   * specified system properties are known to already be set correctly for the
1023   * desired authentication processing.
1024   *
1025   * @param  suppressedSystemProperties  A set of system properties that will
1026   *                                     not be altered by GSSAPI processing.
1027   *                                     It may be {@code null} or empty to
1028   *                                     indicate that no properties should be
1029   *                                     suppressed.
1030   */
1031  public void setSuppressedSystemProperties(
1032       @Nullable final Collection<String> suppressedSystemProperties)
1033  {
1034    if (suppressedSystemProperties == null)
1035    {
1036      this.suppressedSystemProperties = Collections.emptySet();
1037    }
1038    else
1039    {
1040      this.suppressedSystemProperties = Collections.unmodifiableSet(
1041           new LinkedHashSet<>(suppressedSystemProperties));
1042    }
1043  }
1044
1045
1046
1047  /**
1048   * Indicates whether JVM-level debugging should be enabled for GSSAPI bind
1049   * processing.  If this is enabled, then debug information may be written to
1050   * standard error when performing GSSAPI processing that could be useful for
1051   * debugging authentication problems.
1052   *
1053   * @return  {@code true} if JVM-level debugging should be enabled for GSSAPI
1054   *          bind processing, or {@code false} if not.
1055   */
1056  public boolean enableGSSAPIDebugging()
1057  {
1058    return enableGSSAPIDebugging;
1059  }
1060
1061
1062
1063  /**
1064   * Specifies whether JVM-level debugging should be enabled for GSSAPI bind
1065   * processing.  If this is enabled, then debug information may be written to
1066   * standard error when performing GSSAPI processing that could be useful for
1067   * debugging authentication problems.
1068   *
1069   * @param  enableGSSAPIDebugging  Specifies whether JVM-level debugging should
1070   *                                be enabled for GSSAPI bind processing.
1071   */
1072  public void setEnableGSSAPIDebugging(final boolean enableGSSAPIDebugging)
1073  {
1074    this.enableGSSAPIDebugging = enableGSSAPIDebugging;
1075    configFileDigest = null;
1076  }
1077
1078
1079
1080  /**
1081   * Retrieves a digest of the settings that are relevant to a JAAS
1082   * configuration file generated from these properties.
1083   *
1084   * @return  A digest of the settings that are relevant to a JAAS configuration
1085   *          file generated from these properties.
1086   *
1087   * @throws  LDAPException  If a problem occurs while attempting to generate
1088   *                         the digest.
1089   */
1090  @NotNull()
1091  byte[] getConfigFileDigest()
1092         throws LDAPException
1093  {
1094    // If we have previously generated a digest from the current settings, then
1095    // just return that.
1096    if (configFileDigest != null)
1097    {
1098      return configFileDigest;
1099    }
1100
1101
1102    // Generate a JSON object from the relevant settings.
1103    final JSONBuffer buffer = new JSONBuffer();
1104    buffer.beginObject();
1105    buffer.appendString("jaasClientName", jaasClientName);
1106
1107    if (isInitiator != null)
1108    {
1109      buffer.appendBoolean("isInitiator", isInitiator);
1110    }
1111
1112    buffer.appendBoolean("refreshKrb5Config", refreshKrb5Config);
1113    buffer.appendBoolean("useKeyTab", useKeyTab);
1114
1115    if (keyTabPath != null)
1116    {
1117      buffer.appendString("keyTabPath", keyTabPath);
1118    }
1119
1120    buffer.appendBoolean("useTicketCache", useTicketCache);
1121    buffer.appendBoolean("renewTGT", renewTGT);
1122    buffer.appendBoolean("requireCachedCredentials", requireCachedCredentials);
1123
1124    if (ticketCachePath != null)
1125    {
1126      buffer.appendString("ticketCachePath", ticketCachePath);
1127    }
1128
1129    buffer.appendBoolean("enableGSSAPIDebugging", enableGSSAPIDebugging);
1130    buffer.endObject();
1131
1132
1133    // Generate, cache, and return a 256-bit SHA digest of the JSON object.
1134    try
1135    {
1136      final byte[] bufferBytes = buffer.getBuffer().toByteArray();
1137      configFileDigest = CryptoHelper.sha256(bufferBytes);
1138      return configFileDigest;
1139    }
1140    catch (final Exception e)
1141    {
1142      Debug.debugException(e);
1143      throw new LDAPException(ResultCode.LOCAL_ERROR,
1144           ERR_GSSAPI_PROPERTIES_CANNOT_COMPUTE_DIGEST.get(
1145                StaticUtils.getExceptionMessage(e)),
1146           e);
1147
1148    }
1149  }
1150
1151
1152
1153  /**
1154   * Retrieves a string representation of the GSSAPI bind request properties.
1155   *
1156   * @return  A string representation of the GSSAPI bind request properties.
1157   */
1158  @Override()
1159  @NotNull()
1160  public String toString()
1161  {
1162    final StringBuilder buffer = new StringBuilder();
1163    toString(buffer);
1164    return buffer.toString();
1165  }
1166
1167
1168
1169  /**
1170   * Appends a string representation of the GSSAPI bind request properties to
1171   * the provided buffer.
1172   *
1173   * @param  buffer  The buffer to which the information should be appended.
1174   */
1175  public void toString(@NotNull final StringBuilder buffer)
1176  {
1177    buffer.append("GSSAPIBindRequestProperties(");
1178    if (authenticationID != null)
1179    {
1180      buffer.append("authenticationID='");
1181      buffer.append(authenticationID);
1182      buffer.append("', ");
1183    }
1184
1185    if (authorizationID != null)
1186    {
1187      buffer.append("authorizationID='");
1188      buffer.append(authorizationID);
1189      buffer.append("', ");
1190    }
1191
1192    if (realm != null)
1193    {
1194      buffer.append("realm='");
1195      buffer.append(realm);
1196      buffer.append("', ");
1197    }
1198
1199    buffer.append("qop='");
1200    buffer.append(SASLQualityOfProtection.toString(allowedQoP));
1201    buffer.append("', ");
1202
1203    if (kdcAddress != null)
1204    {
1205      buffer.append("kdcAddress='");
1206      buffer.append(kdcAddress);
1207      buffer.append("', ");
1208    }
1209
1210    buffer.append(", refreshKrb5Config=");
1211    buffer.append(refreshKrb5Config);
1212    buffer.append(", useSubjectCredentialsOnly=");
1213    buffer.append(useSubjectCredentialsOnly);
1214    buffer.append(", useKeyTab=");
1215    buffer.append(useKeyTab);
1216    buffer.append(", ");
1217
1218    if (keyTabPath != null)
1219    {
1220      buffer.append("keyTabPath='");
1221      buffer.append(keyTabPath);
1222      buffer.append("', ");
1223    }
1224
1225    if (useTicketCache)
1226    {
1227      buffer.append("useTicketCache=true, requireCachedCredentials=");
1228      buffer.append(requireCachedCredentials);
1229      buffer.append(", renewTGT=");
1230      buffer.append(renewTGT);
1231      buffer.append(", ");
1232
1233      if (ticketCachePath != null)
1234      {
1235        buffer.append("ticketCachePath='");
1236        buffer.append(ticketCachePath);
1237        buffer.append("', ");
1238      }
1239    }
1240    else
1241    {
1242      buffer.append("useTicketCache=false, ");
1243    }
1244
1245    if (isInitiator != null)
1246    {
1247      buffer.append("isInitiator=");
1248      buffer.append(isInitiator);
1249      buffer.append(", ");
1250    }
1251
1252    buffer.append("jaasClientName='");
1253    buffer.append(jaasClientName);
1254    buffer.append("', ");
1255
1256    if (configFilePath != null)
1257    {
1258      buffer.append("configFilePath='");
1259      buffer.append(configFilePath);
1260      buffer.append("', ");
1261    }
1262
1263    if (saslClientServerName != null)
1264    {
1265      buffer.append("saslClientServerName='");
1266      buffer.append(saslClientServerName);
1267      buffer.append("', ");
1268    }
1269
1270    buffer.append("servicePrincipalProtocol='");
1271    buffer.append(servicePrincipalProtocol);
1272    buffer.append("', channelBindingType='");
1273    buffer.append(channelBindingType.getName());
1274    buffer.append("', suppressedSystemProperties={");
1275
1276    final Iterator<String> propIterator = suppressedSystemProperties.iterator();
1277    while (propIterator.hasNext())
1278    {
1279      buffer.append('\'');
1280      buffer.append(propIterator.next());
1281      buffer.append('\'');
1282
1283      if (propIterator.hasNext())
1284      {
1285        buffer.append(", ");
1286      }
1287    }
1288
1289    buffer.append("}, enableGSSAPIDebugging=");
1290    buffer.append(enableGSSAPIDebugging);
1291    buffer.append(')');
1292  }
1293}
1294