jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java
changeset 4531 3a9206343ab2
parent 3864 21eb0b487d1f
child 5458 62f857d96000
equal deleted inserted replaced
4530:cff832a17f52 4531:3a9206343ab2
    29  *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
    29  *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
    30  */
    30  */
    31 
    31 
    32 package sun.security.krb5;
    32 package sun.security.krb5;
    33 
    33 
       
    34 import java.security.AccessController;
       
    35 import java.security.PrivilegedAction;
       
    36 import java.security.Security;
       
    37 import java.util.Locale;
    34 import sun.security.krb5.internal.Krb5;
    38 import sun.security.krb5.internal.Krb5;
    35 import sun.security.krb5.internal.UDPClient;
    39 import sun.security.krb5.internal.UDPClient;
    36 import sun.security.krb5.internal.TCPClient;
    40 import sun.security.krb5.internal.TCPClient;
    37 import java.io.IOException;
    41 import java.io.IOException;
    38 import java.io.InterruptedIOException;
       
    39 import java.net.SocketTimeoutException;
    42 import java.net.SocketTimeoutException;
    40 import java.net.UnknownHostException;
       
    41 import java.util.StringTokenizer;
    43 import java.util.StringTokenizer;
    42 import java.security.AccessController;
    44 import java.security.AccessController;
    43 import java.security.PrivilegedExceptionAction;
    45 import java.security.PrivilegedExceptionAction;
    44 import java.security.PrivilegedActionException;
    46 import java.security.PrivilegedActionException;
       
    47 import java.util.ArrayList;
       
    48 import java.util.List;
       
    49 import java.util.Set;
       
    50 import java.util.HashSet;
    45 
    51 
    46 public abstract class KrbKdcReq {
    52 public abstract class KrbKdcReq {
    47 
       
    48     /**
       
    49      * Default port for a KDC.
       
    50      */
       
    51     private static final int DEFAULT_KDC_PORT = Krb5.KDC_INET_DEFAULT_PORT;
       
    52 
    53 
    53     // Currently there is no option to specify retries
    54     // Currently there is no option to specify retries
    54     // in the kerberos configuration file
    55     // in the kerberos configuration file
    55 
    56 
    56     private static final int DEFAULT_KDC_RETRY_LIMIT = Krb5.KDC_RETRY_LIMIT;
    57     private static final int DEFAULT_KDC_RETRY_LIMIT = Krb5.KDC_RETRY_LIMIT;
    64 
    65 
    65     private static final boolean DEBUG = Krb5.DEBUG;
    66     private static final boolean DEBUG = Krb5.DEBUG;
    66 
    67 
    67     private static int udpPrefLimit = -1;
    68     private static int udpPrefLimit = -1;
    68 
    69 
       
    70     private static final String BAD_POLICY_KEY = "krb5.kdc.bad.policy";
       
    71 
       
    72     /**
       
    73      * What to do when a KDC is unavailable, specified in the
       
    74      * java.security file with key krb5.kdc.bad.policy.
       
    75      * Possible values can be TRY_LAST or TRY_LESS
       
    76      */
       
    77     private enum BpType {
       
    78         NONE, TRY_LAST, TRY_LESS
       
    79     }
       
    80     private static int tryLessMaxRetries = 1;
       
    81     private static int tryLessTimeout = 5000;
       
    82 
       
    83     private static final BpType badPolicy;
       
    84 
    69     static {
    85     static {
       
    86         String value = AccessController.doPrivileged(
       
    87         new PrivilegedAction<String>() {
       
    88             public String run() {
       
    89                 return Security.getProperty(BAD_POLICY_KEY);
       
    90             }
       
    91         });
       
    92         if (value != null) {
       
    93             value = value.toLowerCase(Locale.ENGLISH);
       
    94             String[] ss = value.split(":");
       
    95             if ("tryless".equals(ss[0])) {
       
    96                 if (ss.length > 1) {
       
    97                     String[] params = ss[1].split(",");
       
    98                     tryLessMaxRetries = Integer.parseInt(params[0]);
       
    99                     if (params.length > 1) {
       
   100                         tryLessTimeout = Integer.parseInt(params[1]);
       
   101                     }
       
   102                 }
       
   103                 badPolicy = BpType.TRY_LESS;
       
   104             } else if ("trylast".equals(ss[0])) {
       
   105                 badPolicy = BpType.TRY_LAST;
       
   106             } else {
       
   107                 badPolicy = BpType.NONE;
       
   108             }
       
   109         } else {
       
   110             badPolicy = BpType.NONE;
       
   111         }
    70 
   112 
    71         /*
   113         /*
    72          * Get default timeout.
   114          * Get default timeout.
    73          */
   115          */
    74 
   116 
   129                 throw new KrbException(Krb5.KRB_ERR_GENERIC,
   171                 throw new KrbException(Krb5.KRB_ERR_GENERIC,
   130                                        "Cannot find default realm");
   172                                        "Cannot find default realm");
   131             }
   173             }
   132         }
   174         }
   133 
   175 
   134         /*
       
   135          * Get timeout.
       
   136          */
       
   137 
       
   138         int timeout = getKdcTimeout(realm);
       
   139 
       
   140         String kdcList = cfg.getKDCList(realm);
   176         String kdcList = cfg.getKDCList(realm);
   141         if (kdcList == null) {
   177         if (kdcList == null) {
   142             throw new KrbException("Cannot get kdc for realm " + realm);
   178             throw new KrbException("Cannot get kdc for realm " + realm);
   143         }
   179         }
   144         String tempKdc = null; // may include the port number also
   180         String tempKdc = null; // may include the port number also
   145         StringTokenizer st = new StringTokenizer(kdcList);
   181         for (String tmp: KdcAccessibility.list(kdcList)) {
   146         while (st.hasMoreTokens()) {
   182             tempKdc = tmp;
   147             tempKdc = st.nextToken();
       
   148             try {
   183             try {
   149                 send(realm,tempKdc,useTCP);
   184                 send(realm,tempKdc,useTCP);
       
   185                 KdcAccessibility.removeBad(tempKdc);
   150                 break;
   186                 break;
   151             } catch (Exception e) {
   187             } catch (Exception e) {
   152                 if (DEBUG) {
   188                 if (DEBUG) {
   153                     System.out.println(">>> KrbKdcReq send: error trying " +
   189                     System.out.println(">>> KrbKdcReq send: error trying " +
   154                             tempKdc);
   190                             tempKdc);
   155                     e.printStackTrace(System.out);
   191                     e.printStackTrace(System.out);
   156                 }
   192                 }
       
   193                 KdcAccessibility.addBad(tempKdc);
   157                 savedException = e;
   194                 savedException = e;
   158             }
   195             }
   159         }
   196         }
   160         if (ibuf == null && savedException != null) {
   197         if (ibuf == null && savedException != null) {
   161             if (savedException instanceof IOException) {
   198             if (savedException instanceof IOException) {
   172     public void send(String realm, String tempKdc, boolean useTCP)
   209     public void send(String realm, String tempKdc, boolean useTCP)
   173         throws IOException, KrbException {
   210         throws IOException, KrbException {
   174 
   211 
   175         if (obuf == null)
   212         if (obuf == null)
   176             return;
   213             return;
   177         PrivilegedActionException savedException = null;
   214 
   178         int port = Krb5.KDC_INET_DEFAULT_PORT;
   215         int port = Krb5.KDC_INET_DEFAULT_PORT;
   179 
   216         int retries = DEFAULT_KDC_RETRY_LIMIT;
   180         /*
       
   181          * Get timeout.
       
   182          */
       
   183         int timeout = getKdcTimeout(realm);
   217         int timeout = getKdcTimeout(realm);
   184         /*
   218 
   185          * Get port number for this KDC.
   219         if (badPolicy == BpType.TRY_LESS &&
   186          */
   220                 KdcAccessibility.isBad(tempKdc)) {
       
   221             if (retries > tryLessMaxRetries) {
       
   222                 retries = tryLessMaxRetries; // less retries
       
   223             }
       
   224             if (timeout > tryLessTimeout) {
       
   225                 timeout = tryLessTimeout; // less time
       
   226             }
       
   227         }
       
   228 
   187         String kdc = null;
   229         String kdc = null;
   188         String portStr = null;
   230         String portStr = null;
   189 
   231 
   190         if (tempKdc.charAt(0) == '[') {     // Explicit IPv6 in []
   232         if (tempKdc.charAt(0) == '[') {     // Explicit IPv6 in []
   191             int pos = tempKdc.indexOf(']', 1);
   233             int pos = tempKdc.indexOf(']', 1);
   223             System.out.println(">>> KrbKdcReq send: kdc=" + kdc
   265             System.out.println(">>> KrbKdcReq send: kdc=" + kdc
   224                                + (useTCP ? " TCP:":" UDP:")
   266                                + (useTCP ? " TCP:":" UDP:")
   225                                +  port +  ", timeout="
   267                                +  port +  ", timeout="
   226                                + timeout
   268                                + timeout
   227                                + ", number of retries ="
   269                                + ", number of retries ="
   228                                + DEFAULT_KDC_RETRY_LIMIT
   270                                + retries
   229                                + ", #bytes=" + obuf.length);
   271                                + ", #bytes=" + obuf.length);
   230         }
   272         }
   231 
   273 
   232         KdcCommunication kdcCommunication =
   274         KdcCommunication kdcCommunication =
   233             new KdcCommunication(kdc, port, useTCP, timeout, obuf);
   275             new KdcCommunication(kdc, port, useTCP, timeout, retries, obuf);
   234         try {
   276         try {
   235             ibuf = AccessController.doPrivileged(kdcCommunication);
   277             ibuf = AccessController.doPrivileged(kdcCommunication);
   236             if (DEBUG) {
   278             if (DEBUG) {
   237                 System.out.println(">>> KrbKdcReq send: #bytes read="
   279                 System.out.println(">>> KrbKdcReq send: #bytes read="
   238                         + (ibuf != null ? ibuf.length : 0));
   280                         + (ibuf != null ? ibuf.length : 0));
   256 
   298 
   257         private String kdc;
   299         private String kdc;
   258         private int port;
   300         private int port;
   259         private boolean useTCP;
   301         private boolean useTCP;
   260         private int timeout;
   302         private int timeout;
       
   303         private int retries;
   261         private byte[] obuf;
   304         private byte[] obuf;
   262 
   305 
   263         public KdcCommunication(String kdc, int port, boolean useTCP,
   306         public KdcCommunication(String kdc, int port, boolean useTCP,
   264                                 int timeout, byte[] obuf) {
   307                                 int timeout, int retries, byte[] obuf) {
   265             this.kdc = kdc;
   308             this.kdc = kdc;
   266             this.port = port;
   309             this.port = port;
   267             this.useTCP = useTCP;
   310             this.useTCP = useTCP;
   268             this.timeout = timeout;
   311             this.timeout = timeout;
       
   312             this.retries = retries;
   269             this.obuf = obuf;
   313             this.obuf = obuf;
   270         }
   314         }
   271 
   315 
   272         // The caller only casts IOException and KrbException so don't
   316         // The caller only casts IOException and KrbException so don't
   273         // add any new ones!
   317         // add any new ones!
   292                 }
   336                 }
   293 
   337 
   294             } else {
   338             } else {
   295                 // For each KDC we try DEFAULT_KDC_RETRY_LIMIT (3) times to
   339                 // For each KDC we try DEFAULT_KDC_RETRY_LIMIT (3) times to
   296                 // get the response
   340                 // get the response
   297                 for (int i=1; i <= DEFAULT_KDC_RETRY_LIMIT; i++) {
   341                 for (int i=1; i <= retries; i++) {
   298                     UDPClient kdcClient = new UDPClient(kdc, port, timeout);
   342                     UDPClient kdcClient = new UDPClient(kdc, port, timeout);
   299 
   343 
   300                     if (DEBUG) {
   344                     if (DEBUG) {
   301                         System.out.println(">>> KDCCommunication: kdc=" + kdc
   345                         System.out.println(">>> KDCCommunication: kdc=" + kdc
   302                                + (useTCP ? " TCP:":" UDP:")
   346                                + (useTCP ? " TCP:":" UDP:")
   308                     try {
   352                     try {
   309                         /*
   353                         /*
   310                          * Send the data to the kdc.
   354                          * Send the data to the kdc.
   311                          */
   355                          */
   312 
   356 
   313                     kdcClient.send(obuf);
   357                         kdcClient.send(obuf);
   314 
   358 
   315                         /*
   359                         /*
   316                          * And get a response.
   360                          * And get a response.
   317                          */
   361                          */
   318                         try {
   362                         try {
   321                         } catch (SocketTimeoutException se) {
   365                         } catch (SocketTimeoutException se) {
   322                             if (DEBUG) {
   366                             if (DEBUG) {
   323                                 System.out.println ("SocketTimeOutException with " +
   367                                 System.out.println ("SocketTimeOutException with " +
   324                                                     "attempt: " + i);
   368                                                     "attempt: " + i);
   325                             }
   369                             }
   326                             if (i == DEFAULT_KDC_RETRY_LIMIT) {
   370                             if (i == retries) {
   327                                 ibuf = null;
   371                                 ibuf = null;
   328                                 throw se;
   372                                 throw se;
   329                             }
   373                             }
   330                         }
   374                         }
   331                     } finally {
   375                     } finally {
   383         if (ret >= 0)
   427         if (ret >= 0)
   384             return ret;
   428             return ret;
   385 
   429 
   386         return -1;
   430         return -1;
   387     }
   431     }
       
   432 
       
   433     /**
       
   434      * Maintains a KDC accessible list. Unavailable KDCs are put into a
       
   435      * blacklist, when a KDC in the blacklist is available, it's removed
       
   436      * from there. No insertion order in the blacklist.
       
   437      *
       
   438      * There are two methods to deal with KDCs in the blacklist. 1. Only try
       
   439      * them when there's no KDC not on the blacklist. 2. Still try them, but
       
   440      * with lesser number of retries and smaller timeout value.
       
   441      */
       
   442     static class KdcAccessibility {
       
   443         // Known bad KDCs
       
   444         private static Set<String> bads = new HashSet<String>();
       
   445 
       
   446         private static synchronized void addBad(String kdc) {
       
   447             if (DEBUG) {
       
   448                 System.out.println(">>> KdcAccessibility: add " + kdc);
       
   449             }
       
   450             bads.add(kdc);
       
   451         }
       
   452 
       
   453         private static synchronized void removeBad(String kdc) {
       
   454             if (DEBUG) {
       
   455                 System.out.println(">>> KdcAccessibility: remove " + kdc);
       
   456             }
       
   457             bads.remove(kdc);
       
   458         }
       
   459 
       
   460         private static synchronized boolean isBad(String kdc) {
       
   461             return bads.contains(kdc);
       
   462         }
       
   463 
       
   464         public static synchronized void reset() {
       
   465             if (DEBUG) {
       
   466                 System.out.println(">>> KdcAccessibility: reset");
       
   467             }
       
   468             bads.clear();
       
   469         }
       
   470 
       
   471         // Returns a preferred KDC list by putting the bad ones at the end
       
   472         private static synchronized String[] list(String kdcList) {
       
   473             StringTokenizer st = new StringTokenizer(kdcList);
       
   474             List<String> list = new ArrayList<String>();
       
   475             if (badPolicy == BpType.TRY_LAST) {
       
   476                 List<String> badkdcs = new ArrayList<String>();
       
   477                 while (st.hasMoreTokens()) {
       
   478                     String t = st.nextToken();
       
   479                     if (bads.contains(t)) badkdcs.add(t);
       
   480                     else list.add(t);
       
   481                 }
       
   482                 // Bad KDCs are put at last
       
   483                 list.addAll(badkdcs);
       
   484             } else {
       
   485                 // All KDCs are returned in their original order,
       
   486                 // This include TRY_LESS and NONE
       
   487                 while (st.hasMoreTokens()) {
       
   488                     list.add(st.nextToken());
       
   489                 }
       
   490             }
       
   491             return list.toArray(new String[list.size()]);
       
   492         }
       
   493     }
   388 }
   494 }
       
   495