1 19 20 package jcifs.http; 21 22 import java.io.ByteArrayOutputStream ; 23 import java.io.InputStream ; 24 import java.io.IOException ; 25 import java.io.OutputStream ; 26 27 import java.net.Authenticator ; 28 import java.net.HttpURLConnection ; 29 import java.net.PasswordAuthentication ; 30 import java.net.ProtocolException ; 31 import java.net.URL ; 32 import java.net.URLDecoder ; 33 34 import java.security.Permission ; 35 36 import java.util.ArrayList ; 37 import java.util.Collections ; 38 import java.util.HashMap ; 39 import java.util.Iterator ; 40 import java.util.List ; 41 import java.util.Map ; 42 43 import jcifs.Config; 44 45 import jcifs.ntlmssp.NtlmFlags; 46 import jcifs.ntlmssp.NtlmMessage; 47 import jcifs.ntlmssp.Type1Message; 48 import jcifs.ntlmssp.Type2Message; 49 import jcifs.ntlmssp.Type3Message; 50 51 import jcifs.util.Base64; 52 53 59 public class NtlmHttpURLConnection extends HttpURLConnection { 60 61 private static final int MAX_REDIRECTS = 62 Integer.parseInt(System.getProperty("http.maxRedirects", "20")); 63 64 private static final int LM_COMPATIBILITY = 65 Config.getInt("jcifs.smb.lmCompatibility", 0); 66 67 private static final String DEFAULT_DOMAIN; 68 69 private HttpURLConnection connection; 70 71 private Map requestProperties; 72 73 private Map headerFields; 74 75 private ByteArrayOutputStream cachedOutput; 76 77 private String authProperty; 78 79 private String authMethod; 80 81 private boolean handshakeComplete; 82 83 static { 84 String domain = System.getProperty("http.auth.ntlm.domain"); 85 if (domain == null) domain = Type3Message.getDefaultDomain(); 86 DEFAULT_DOMAIN = domain; 87 } 88 89 public NtlmHttpURLConnection(HttpURLConnection connection) { 90 super(connection.getURL()); 91 this.connection = connection; 92 requestProperties = new HashMap (); 93 } 94 95 public void connect() throws IOException { 96 if (connected) return; 97 connection.connect(); 98 connected = true; 99 } 100 101 private void handshake() throws IOException { 102 if (handshakeComplete) return; 103 doHandshake(); 104 handshakeComplete = true; 105 } 106 107 public URL getURL() { 108 return connection.getURL(); 109 } 110 111 public int getContentLength() { 112 try { 113 handshake(); 114 } catch (IOException ex) { } 115 return connection.getContentLength(); 116 } 117 118 public String getContentType() { 119 try { 120 handshake(); 121 } catch (IOException ex) { } 122 return connection.getContentType(); 123 } 124 125 public String getContentEncoding() { 126 try { 127 handshake(); 128 } catch (IOException ex) { } 129 return connection.getContentEncoding(); 130 } 131 132 public long getExpiration() { 133 try { 134 handshake(); 135 } catch (IOException ex) { } 136 return connection.getExpiration(); 137 } 138 139 public long getDate() { 140 try { 141 handshake(); 142 } catch (IOException ex) { } 143 return connection.getDate(); 144 } 145 146 public long getLastModified() { 147 try { 148 handshake(); 149 } catch (IOException ex) { } 150 return connection.getLastModified(); 151 } 152 153 public String getHeaderField(String header) { 154 try { 155 handshake(); 156 } catch (IOException ex) { } 157 return connection.getHeaderField(header); 158 } 159 160 private Map getHeaderFields0() { 161 if (headerFields != null) return headerFields; 162 Map map = new HashMap (); 163 String key = connection.getHeaderFieldKey(0); 164 String value = connection.getHeaderField(0); 165 for (int i = 1; key != null || value != null; i++) { 166 List values = (List ) map.get(key); 167 if (values == null) { 168 values = new ArrayList (); 169 map.put(key, values); 170 } 171 values.add(value); 172 key = connection.getHeaderFieldKey(i); 173 value = connection.getHeaderField(i); 174 } 175 Iterator entries = map.entrySet().iterator(); 176 while (entries.hasNext()) { 177 Map.Entry entry = (Map.Entry ) entries.next(); 178 entry.setValue(Collections.unmodifiableList((List ) 179 entry.getValue())); 180 } 181 return (headerFields = Collections.unmodifiableMap(map)); 182 } 183 184 public Map getHeaderFields() { 185 if (headerFields != null) return headerFields; 186 try { 187 handshake(); 188 } catch (IOException ex) { } 189 return getHeaderFields0(); 190 } 191 192 public int getHeaderFieldInt(String header, int def) { 193 try { 194 handshake(); 195 } catch (IOException ex) { } 196 return connection.getHeaderFieldInt(header, def); 197 } 198 199 public long getHeaderFieldDate(String header, long def) { 200 try { 201 handshake(); 202 } catch (IOException ex) { } 203 return connection.getHeaderFieldDate(header, def); 204 } 205 206 public String getHeaderFieldKey(int index) { 207 try { 208 handshake(); 209 } catch (IOException ex) { } 210 return connection.getHeaderFieldKey(index); 211 } 212 213 public String getHeaderField(int index) { 214 try { 215 handshake(); 216 } catch (IOException ex) { } 217 return connection.getHeaderField(index); 218 } 219 220 public Object getContent() throws IOException { 221 try { 222 handshake(); 223 } catch (IOException ex) { } 224 return connection.getContent(); 225 } 226 227 public Object getContent(Class [] classes) throws IOException { 228 try { 229 handshake(); 230 } catch (IOException ex) { } 231 return connection.getContent(classes); 232 } 233 234 public Permission getPermission() throws IOException { 235 return connection.getPermission(); 236 } 237 238 public InputStream getInputStream() throws IOException { 239 try { 240 handshake(); 241 } catch (IOException ex) { } 242 return connection.getInputStream(); 243 } 244 245 public OutputStream getOutputStream() throws IOException { 246 try { 247 connect(); 248 } catch (IOException ex) { } 249 OutputStream output = connection.getOutputStream(); 250 cachedOutput = new ByteArrayOutputStream (); 251 return new CacheStream(output, cachedOutput); 252 } 253 254 public String toString() { 255 return connection.toString(); 256 } 257 258 public void setDoInput(boolean doInput) { 259 connection.setDoInput(doInput); 260 this.doInput = doInput; 261 } 262 263 public boolean getDoInput() { 264 return connection.getDoInput(); 265 } 266 267 public void setDoOutput(boolean doOutput) { 268 connection.setDoOutput(doOutput); 269 this.doOutput = doOutput; 270 } 271 272 public boolean getDoOutput() { 273 return connection.getDoOutput(); 274 } 275 276 public void setAllowUserInteraction(boolean allowUserInteraction) { 277 connection.setAllowUserInteraction(allowUserInteraction); 278 this.allowUserInteraction = allowUserInteraction; 279 } 280 281 public boolean getAllowUserInteraction() { 282 return connection.getAllowUserInteraction(); 283 } 284 285 public void setUseCaches(boolean useCaches) { 286 connection.setUseCaches(useCaches); 287 this.useCaches = useCaches; 288 } 289 290 public boolean getUseCaches() { 291 return connection.getUseCaches(); 292 } 293 294 public void setIfModifiedSince(long ifModifiedSince) { 295 connection.setIfModifiedSince(ifModifiedSince); 296 this.ifModifiedSince = ifModifiedSince; 297 } 298 299 public long getIfModifiedSince() { 300 return connection.getIfModifiedSince(); 301 } 302 303 public boolean getDefaultUseCaches() { 304 return connection.getDefaultUseCaches(); 305 } 306 307 public void setDefaultUseCaches(boolean defaultUseCaches) { 308 connection.setDefaultUseCaches(defaultUseCaches); 309 } 310 311 public void setRequestProperty(String key, String value) { 312 if (key == null) throw new NullPointerException (); 313 List values = new ArrayList (); 314 values.add(value); 315 boolean found = false; 316 Iterator entries = requestProperties.entrySet().iterator(); 317 while (entries.hasNext()) { 318 Map.Entry entry = (Map.Entry ) entries.next(); 319 if (key.equalsIgnoreCase((String ) entry.getKey())) { 320 entry.setValue(value); 321 found = true; 322 break; 323 } 324 } 325 if (!found) requestProperties.put(key, values); 326 connection.setRequestProperty(key, value); 327 } 328 329 public void addRequestProperty(String key, String value) { 330 if (key == null) throw new NullPointerException (); 331 List values = null; 332 Iterator entries = requestProperties.entrySet().iterator(); 333 while (entries.hasNext()) { 334 Map.Entry entry = (Map.Entry ) entries.next(); 335 if (key.equalsIgnoreCase((String ) entry.getKey())) { 336 values = (List ) entry.getValue(); 337 values.add(value); 338 break; 339 } 340 } 341 if (values == null) { 342 values = new ArrayList (); 343 values.add(value); 344 requestProperties.put(key, values); 345 } 346 StringBuffer buffer = new StringBuffer (); 348 Iterator propertyValues = values.iterator(); 349 while (propertyValues.hasNext()) { 350 buffer.append(propertyValues.next()); 351 if (propertyValues.hasNext()) { 352 buffer.append(", "); 353 } 354 } 355 connection.setRequestProperty(key, buffer.toString()); 356 } 357 358 public String getRequestProperty(String key) { 359 return connection.getRequestProperty(key); 360 } 361 362 public Map getRequestProperties() { 363 Map map = new HashMap (); 364 Iterator entries = requestProperties.entrySet().iterator(); 365 while (entries.hasNext()) { 366 Map.Entry entry = (Map.Entry ) entries.next(); 367 map.put(entry.getKey(), 368 Collections.unmodifiableList((List ) entry.getValue())); 369 } 370 return Collections.unmodifiableMap(map); 371 } 372 373 public void setInstanceFollowRedirects(boolean instanceFollowRedirects) { 374 connection.setInstanceFollowRedirects(instanceFollowRedirects); 375 } 376 377 public boolean getInstanceFollowRedirects() { 378 return connection.getInstanceFollowRedirects(); 379 } 380 381 public void setRequestMethod(String requestMethod) 382 throws ProtocolException { 383 connection.setRequestMethod(requestMethod); 384 this.method = requestMethod; 385 } 386 387 public String getRequestMethod() { 388 return connection.getRequestMethod(); 389 } 390 391 public int getResponseCode() throws IOException { 392 try { 393 handshake(); 394 } catch (IOException ex) { } 395 return connection.getResponseCode(); 396 } 397 398 public String getResponseMessage() throws IOException { 399 try { 400 handshake(); 401 } catch (IOException ex) { } 402 return connection.getResponseMessage(); 403 } 404 405 public void disconnect() { 406 connection.disconnect(); 407 handshakeComplete = false; 408 connected = false; 409 } 410 411 public boolean usingProxy() { 412 return connection.usingProxy(); 413 } 414 415 public InputStream getErrorStream() { 416 try { 417 handshake(); 418 } catch (IOException ex) { } 419 return connection.getErrorStream(); 420 } 421 422 private int parseResponseCode() throws IOException { 423 try { 424 String response = connection.getHeaderField(0); 425 int index = response.indexOf(' '); 426 while (response.charAt(index) == ' ') index++; 427 return Integer.parseInt(response.substring(index, index + 3)); 428 } catch (Exception ex) { 429 throw new IOException (ex.getMessage()); 430 } 431 } 432 433 private void doHandshake() throws IOException { 434 connect(); 435 try { 436 int response = parseResponseCode(); 437 if (response != HTTP_UNAUTHORIZED && response != HTTP_PROXY_AUTH) { 438 return; 439 } 440 Type1Message type1 = (Type1Message) attemptNegotiation(response); 441 if (type1 == null) return; int attempt = 0; 443 while (attempt < MAX_REDIRECTS) { 444 connection.setRequestProperty(authProperty, authMethod + ' ' + 445 Base64.encode(type1.toByteArray())); 446 connection.connect(); response = parseResponseCode(); 448 if (response != HTTP_UNAUTHORIZED && 449 response != HTTP_PROXY_AUTH) { 450 return; 451 } 452 Type3Message type3 = (Type3Message) 453 attemptNegotiation(response); 454 if (type3 == null) return; 455 connection.setRequestProperty(authProperty, authMethod + ' ' + 456 Base64.encode(type3.toByteArray())); 457 connection.connect(); if (cachedOutput != null && doOutput) { 459 OutputStream output = connection.getOutputStream(); 460 cachedOutput.writeTo(output); 461 output.flush(); 462 } 463 response = parseResponseCode(); 464 if (response != HTTP_UNAUTHORIZED && 465 response != HTTP_PROXY_AUTH) { 466 return; 467 } 468 attempt++; 469 if (allowUserInteraction && attempt < MAX_REDIRECTS) { 470 reconnect(); 471 } else { 472 break; 473 } 474 } 475 throw new IOException ("Unable to negotiate NTLM authentication."); 476 } finally { 477 cachedOutput = null; 478 } 479 } 480 481 private NtlmMessage attemptNegotiation(int response) throws IOException { 482 authProperty = null; 483 authMethod = null; 484 InputStream errorStream = connection.getErrorStream(); 485 if (errorStream != null && errorStream.available() != 0) { 486 int count; 487 byte[] buf = new byte[1024]; 488 while ((count = errorStream.read(buf, 0, 1024)) != -1); 489 } 490 String authHeader; 491 if (response == HTTP_UNAUTHORIZED) { 492 authHeader = "WWW-Authenticate"; 493 authProperty = "Authorization"; 494 } else { 495 authHeader = "Proxy-Authenticate"; 496 authProperty = "Proxy-Authorization"; 497 } 498 String authorization = null; 499 List methods = (List ) getHeaderFields0().get(authHeader); 500 if (methods == null) return null; 501 Iterator iterator = methods.iterator(); 502 while (iterator.hasNext()) { 503 String currentAuthMethod = (String ) iterator.next(); 504 if (currentAuthMethod.startsWith("NTLM")) { 505 if (currentAuthMethod.length() == 4) { 506 authMethod = "NTLM"; 507 break; 508 } 509 if (currentAuthMethod.indexOf(' ') != 4) continue; 510 authMethod = "NTLM"; 511 authorization = currentAuthMethod.substring(5).trim(); 512 break; 513 } else if (currentAuthMethod.startsWith("Negotiate")) { 514 if (currentAuthMethod.length() == 9) { 515 authMethod = "Negotiate"; 516 break; 517 } 518 if (currentAuthMethod.indexOf(' ') != 9) continue; 519 authMethod = "Negotiate"; 520 authorization = currentAuthMethod.substring(10).trim(); 521 break; 522 } 523 } 524 if (authMethod == null) return null; 525 NtlmMessage message = (authorization != null) ? 526 new Type2Message(Base64.decode(authorization)) : null; 527 reconnect(); 528 if (message == null) { 529 message = new Type1Message(); 530 if (LM_COMPATIBILITY > 2) { 531 message.setFlag(NtlmFlags.NTLMSSP_REQUEST_TARGET, true); 532 } 533 } else { 534 String domain = DEFAULT_DOMAIN; 535 String user = Type3Message.getDefaultUser(); 536 String password = Type3Message.getDefaultPassword(); 537 String userInfo = url.getUserInfo(); 538 if (userInfo != null) { 539 userInfo = URLDecoder.decode(userInfo); 540 int index = userInfo.indexOf(':'); 541 user = (index != -1) ? userInfo.substring(0, index) : userInfo; 542 if (index != -1) password = userInfo.substring(index + 1); 543 index = user.indexOf('\\'); 544 if (index == -1) index = user.indexOf('/'); 545 domain = (index != -1) ? user.substring(0, index) : domain; 546 user = (index != -1) ? user.substring(index + 1) : user; 547 } 548 if (user == null) { 549 if (!allowUserInteraction) return null; 550 try { 551 URL url = getURL(); 552 String protocol = url.getProtocol(); 553 int port = url.getPort(); 554 if (port == -1) { 555 port = "https".equalsIgnoreCase(protocol) ? 443 : 80; 556 } 557 PasswordAuthentication auth = 558 Authenticator.requestPasswordAuthentication(null, 559 port, protocol, "", authMethod); 560 if (auth == null) return null; 561 user = auth.getUserName(); 562 password = new String (auth.getPassword()); 563 } catch (Exception ex) { } 564 } 565 Type2Message type2 = (Type2Message) message; 566 message = new Type3Message(type2, password, domain, user, 567 Type3Message.getDefaultWorkstation()); 568 } 569 return message; 570 } 571 572 private void reconnect() throws IOException { 573 connection = (HttpURLConnection ) connection.getURL().openConnection(); 574 connection.setRequestMethod(method); 575 headerFields = null; 576 Iterator properties = requestProperties.entrySet().iterator(); 577 while (properties.hasNext()) { 578 Map.Entry property = (Map.Entry ) properties.next(); 579 String key = (String ) property.getKey(); 580 StringBuffer value = new StringBuffer (); 581 Iterator values = ((List ) property.getValue()).iterator(); 582 while (values.hasNext()) { 583 value.append(values.next()); 584 if (values.hasNext()) value.append(", "); 585 } 586 connection.setRequestProperty(key, value.toString()); 587 } 588 connection.setAllowUserInteraction(allowUserInteraction); 589 connection.setDoInput(doInput); 590 connection.setDoOutput(doOutput); 591 connection.setIfModifiedSince(ifModifiedSince); 592 connection.setUseCaches(useCaches); 593 } 594 595 private static class CacheStream extends OutputStream { 596 597 private final OutputStream stream; 598 599 private final OutputStream collector; 600 601 public CacheStream(OutputStream stream, OutputStream collector) { 602 this.stream = stream; 603 this.collector = collector; 604 } 605 606 public void close() throws IOException { 607 stream.close(); 608 collector.close(); 609 } 610 611 public void flush() throws IOException { 612 stream.flush(); 613 collector.flush(); 614 } 615 616 public void write(byte[] b) throws IOException { 617 stream.write(b); 618 collector.write(b); 619 } 620 621 public void write(byte[] b, int off, int len) throws IOException { 622 stream.write(b, off, len); 623 collector.write(b, off, len); 624 } 625 626 public void write(int b) throws IOException { 627 stream.write(b); 628 collector.write(b); 629 } 630 631 } 632 633 } 634 | Popular Tags |