8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.net.www.protocol.http; 27 28 import java.util.HashMap; 29 30 import sun.net.www.HeaderParser; 31 import sun.misc.BASE64Decoder; 32 import sun.misc.BASE64Encoder; 33 import sun.util.logging.PlatformLogger; 34 35 import java.net.URL; 36 import java.io.IOException; 37 import java.net.Authenticator.RequestorType; 38 import java.lang.reflect.Constructor; 39 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; 40 import static sun.net.www.protocol.http.AuthScheme.KERBEROS; 41 42 /** 43 * NegotiateAuthentication: 44 * 45 * @author weijun.wang@sun.com 46 * @since 1.6 47 */ 48 49 class NegotiateAuthentication extends AuthenticationInfo { 50 51 private static final long serialVersionUID = 100L; 52 53 final private HttpCallerInfo hci; 54 55 // These maps are used to manage the GSS availability for diffrent 56 // hosts. The key for both maps is the host name. 57 // <code>supported</code> is set when isSupported is checked, 58 // if it's true, a cached Negotiator is put into <code>cache</code>. 61 static HashMap <String, Negotiator> cache = null; 62 63 // The HTTP Negotiate Helper 64 private Negotiator negotiator = null; 65 66 /** 67 * Constructor used for both WWW and proxy entries. 68 * @param hci a schemed object. 69 */ 70 public NegotiateAuthentication(HttpCallerInfo hci) { 71 super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, 72 hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS, 73 hci.url, 74 ""); 75 this.hci = hci; 76 } 77 78 /** 79 * @return true if this authentication supports preemptive authorization 80 */ 81 boolean supportsPreemptiveAuthorization() { 82 return false; 83 } 84 85 /** 86 * Find out if the HttpCallerInfo supports Negotiate protocol. In order to 87 * find out yes or no, an initialization of a Negotiator object against it 88 * is tried. The generated object will be cached under the name of ths 89 * hostname at a success try.<br> 90 * 91 * If this method is called for the second time on an HttpCallerInfo with 92 * the same hostname, the answer is retrieved from cache. 93 * 94 * @return true if supported 95 */ 96 synchronized public static boolean isSupported(HttpCallerInfo hci) { 97 if (supported == null) { 98 supported = new HashMap <String, Boolean>(); 99 cache = new HashMap <String, Negotiator>(); 100 } 101 String hostname = hci.host; 102 hostname = hostname.toLowerCase(); 103 if (supported.containsKey(hostname)) { 104 return supported.get(hostname); 105 } 106 107 try { 108 Negotiator neg = Negotiator.getSupported(hci); 109 supported.put(hostname, true); 110 // the only place cache.put is called. here we can make sure 111 // the object is valid and the oneToken inside is not null 112 cache.put(hostname, neg); 113 return true; 114 } catch(Exception e) { 115 supported.put(hostname, false); 116 return false; 117 } 118 } 119 120 /** 121 * @return the name of the HTTP header this authentication wants to set 122 */ 123 String getHeaderName() { 124 if (type == SERVER_AUTHENTICATION) { 125 return "Authorization"; 126 } else { 127 return "Proxy-Authorization"; 128 } 129 } 130 131 /** 132 * Not supported. Must use the setHeaders() method 133 */ 134 String getHeaderValue(URL url, String method) { 135 throw new RuntimeException ("getHeaderValue not supported"); 136 } 137 138 /** 139 * Check if the header indicates that the current auth. parameters are stale. 140 * If so, then replace the relevant field with the new value 141 * and return true. Otherwise return false. 142 * returning true means the request can be retried with the same userid/password 143 * returning false means we have to go back to the user to ask for a new 144 * username password. 145 */ 146 boolean isAuthorizationStale (String header) { 147 return false; /* should not be called for Negotiate */ 148 } 149 150 /** 151 * Set header(s) on the given connection. 152 * @param conn The connection to apply the header(s) to 153 * @param p A source of header values for this connection, not used because 154 * HeaderParser converts the fields to lower case, use raw instead 155 * @param raw The raw header field. 156 * @return true if all goes well, false if no headers were set. 157 */ 158 synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { 159 160 try { 161 String response; 162 byte[] incoming = null; 163 String[] parts = raw.split("\\s+"); 164 if (parts.length > 1) { 165 incoming = new BASE64Decoder().decodeBuffer(parts[1]); 166 } 167 response = hci.scheme + " " + new B64Encoder().encode( 168 incoming==null?firstToken():nextToken(incoming)); 169 170 conn.setAuthenticationProperty(getHeaderName(), response); 171 return true; 172 } catch (IOException e) { 173 return false; 174 } 175 } 176 177 /** 178 * return the first token. 210 * @throws IOException if <code>negotiator.nextToken()</code> throws Exception. 211 * May happen if the input token is invalid. 212 */ 213 private byte[] nextToken(byte[] token) throws IOException { 214 return negotiator.nextToken(token); 215 } 216 217 class B64Encoder extends BASE64Encoder { 218 protected int bytesPerLine () { 219 return 100000; // as big as it can be, maybe INT_MAX 220 } 221 } 222 223 // MS will send a final WWW-Authenticate even if the status is already 224 // 200 OK. The token can be fed into initSecContext() again to determine 225 // if the server can be trusted. This is not the same concept as Digest's 226 // Authentication-Info header. 227 // 228 // Currently we ignore this header. 229 230 } 231 232 /** 233 * This abstract class is a bridge to connect NegotiteAuthentication and 234 * NegotiatorImpl, so that JAAS and JGSS calls can be made 235 */ 236 abstract class Negotiator { 237 static Negotiator getSupported(HttpCallerInfo hci) 238 throws Exception { 239 240 // These lines are equivalent to 241 // return new NegotiatorImpl(hci); 242 // The current implementation will make sure NegotiatorImpl is not 243 // directly referenced when compiling, thus smooth the way of building 244 // the J2SE platform where HttpURLConnection is a bootstrap class. 245 // 246 // Makes NegotiatorImpl, and the security classes it references, a 247 // runtime dependency rather than a static one. 248 249 Class clazz; 250 Constructor c; 251 try { 252 clazz = Class.forName("sun.net.www.protocol.http.NegotiatorImpl", true, null); 253 c = clazz.getConstructor(HttpCallerInfo.class); 254 } catch (ClassNotFoundException cnfe) { 255 finest(cnfe); 256 throw cnfe; 257 } catch (ReflectiveOperationException roe) { 258 // if the class is there then something seriously wrong if 259 // the constructor is not. 260 throw new AssertionError(roe); 261 } 262 263 try { 264 return (Negotiator) (c.newInstance(hci)); 265 } catch (ReflectiveOperationException roe) { 266 finest(roe); 267 Throwable t = roe.getCause(); 268 if (t != null && t instanceof Exception) 269 finest((Exception)t); 270 throw roe; 271 } 272 } 273 274 abstract byte[] firstToken() throws IOException; 275 276 abstract byte[] nextToken(byte[] in) throws IOException; 277 278 static void finest(Exception e) { 279 PlatformLogger logger = HttpURLConnection.getHttpLogger(); 280 logger.finest("NegotiateAuthentication: " + e); 281 } 282 } | 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.net.www.protocol.http; 27 28 import java.net.URL; 29 import java.io.IOException; 30 import java.net.Authenticator.RequestorType; 31 import java.util.HashMap; 32 import sun.net.www.HeaderParser; 33 import sun.misc.BASE64Decoder; 34 import sun.misc.BASE64Encoder; 35 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; 36 import static sun.net.www.protocol.http.AuthScheme.KERBEROS; 37 38 /** 39 * NegotiateAuthentication: 40 * 41 * @author weijun.wang@sun.com 42 * @since 1.6 43 */ 44 45 class NegotiateAuthentication extends AuthenticationInfo { 46 47 private static final long serialVersionUID = 100L; 48 49 final private HttpCallerInfo hci; 50 51 // These maps are used to manage the GSS availability for diffrent 52 // hosts. The key for both maps is the host name. 53 // <code>supported</code> is set when isSupported is checked, 54 // if it's true, a cached Negotiator is put into <code>cache</code>. 57 static HashMap <String, Negotiator> cache = null; 58 59 // The HTTP Negotiate Helper 60 private Negotiator negotiator = null; 61 62 /** 63 * Constructor used for both WWW and proxy entries. 64 * @param hci a schemed object. 65 */ 66 public NegotiateAuthentication(HttpCallerInfo hci) { 67 super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, 68 hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS, 69 hci.url, 70 ""); 71 this.hci = hci; 72 } 73 74 /** 75 * @return true if this authentication supports preemptive authorization 76 */ 77 @Override 78 public boolean supportsPreemptiveAuthorization() { 79 return false; 80 } 81 82 /** 83 * Find out if the HttpCallerInfo supports Negotiate protocol. In order to 84 * find out yes or no, an initialization of a Negotiator object against it 85 * is tried. The generated object will be cached under the name of ths 86 * hostname at a success try.<br> 87 * 88 * If this method is called for the second time on an HttpCallerInfo with 89 * the same hostname, the answer is retrieved from cache. 90 * 91 * @return true if supported 92 */ 93 synchronized public static boolean isSupported(HttpCallerInfo hci) { 94 if (supported == null) { 95 supported = new HashMap <String, Boolean>(); 96 cache = new HashMap <String, Negotiator>(); 97 } 98 String hostname = hci.host; 99 hostname = hostname.toLowerCase(); 100 if (supported.containsKey(hostname)) { 101 return supported.get(hostname); 102 } 103 104 try { 105 Negotiator neg = Negotiator.getSupported(hci); 106 supported.put(hostname, true); 107 // the only place cache.put is called. here we can make sure 108 // the object is valid and the oneToken inside is not null 109 cache.put(hostname, neg); 110 return true; 111 } catch(Exception e) { 112 supported.put(hostname, false); 113 return false; 114 } 115 } 116 117 /** 118 * Not supported. Must use the setHeaders() method 119 */ 120 @Override 121 public String getHeaderValue(URL url, String method) { 122 throw new RuntimeException ("getHeaderValue not supported"); 123 } 124 125 /** 126 * Check if the header indicates that the current auth. parameters are stale. 127 * If so, then replace the relevant field with the new value 128 * and return true. Otherwise return false. 129 * returning true means the request can be retried with the same userid/password 130 * returning false means we have to go back to the user to ask for a new 131 * username password. 132 */ 133 @Override 134 public boolean isAuthorizationStale (String header) { 135 return false; /* should not be called for Negotiate */ 136 } 137 138 /** 139 * Set header(s) on the given connection. 140 * @param conn The connection to apply the header(s) to 141 * @param p A source of header values for this connection, not used because 142 * HeaderParser converts the fields to lower case, use raw instead 143 * @param raw The raw header field. 144 * @return true if all goes well, false if no headers were set. 145 */ 146 @Override 147 public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { 148 149 try { 150 String response; 151 byte[] incoming = null; 152 String[] parts = raw.split("\\s+"); 153 if (parts.length > 1) { 154 incoming = new BASE64Decoder().decodeBuffer(parts[1]); 155 } 156 response = hci.scheme + " " + new B64Encoder().encode( 157 incoming==null?firstToken():nextToken(incoming)); 158 159 conn.setAuthenticationProperty(getHeaderName(), response); 160 return true; 161 } catch (IOException e) { 162 return false; 163 } 164 } 165 166 /** 167 * return the first token. 199 * @throws IOException if <code>negotiator.nextToken()</code> throws Exception. 200 * May happen if the input token is invalid. 201 */ 202 private byte[] nextToken(byte[] token) throws IOException { 203 return negotiator.nextToken(token); 204 } 205 206 class B64Encoder extends BASE64Encoder { 207 protected int bytesPerLine () { 208 return 100000; // as big as it can be, maybe INT_MAX 209 } 210 } 211 212 // MS will send a final WWW-Authenticate even if the status is already 213 // 200 OK. The token can be fed into initSecContext() again to determine 214 // if the server can be trusted. This is not the same concept as Digest's 215 // Authentication-Info header. 216 // 217 // Currently we ignore this header. 218 219 } |