1 28 29 package HTTPClient; 30 31 32 import java.io.IOException ; 33 import java.io.FileInputStream ; 34 import java.io.DataInputStream ; 35 import java.util.Vector ; 36 import java.util.StringTokenizer ; 37 38 import java.awt.Frame ; 39 import java.awt.Panel ; 40 import java.awt.Label ; 41 import java.awt.Button ; 42 import java.awt.Dimension ; 43 import java.awt.TextField ; 44 import java.awt.GridLayout ; 45 import java.awt.BorderLayout ; 46 import java.awt.GridBagLayout ; 47 import java.awt.GridBagConstraints ; 48 import java.awt.event.ActionEvent ; 49 import java.awt.event.ActionListener ; 50 import java.awt.event.WindowEvent ; 51 import java.awt.event.WindowAdapter ; 52 53 63 64 class DefaultAuthHandler implements AuthorizationHandler, GlobalConstants 65 { 66 private static final byte[] NUL = new byte[0]; 67 68 private static final int DI_A1 = 0; 69 private static final int DI_A1S = 1; 70 private static final int DI_QOP = 2; 71 72 private static byte[] digest_secret = null; 73 74 private BasicAuthBox inp = null; 75 76 77 78 82 public AuthorizationInfo fixupAuthInfo(AuthorizationInfo info, 83 RoRequest req, 84 AuthorizationInfo challenge, 85 RoResponse resp) 86 throws AuthSchemeNotImplException 87 { 88 90 if (info.getScheme().equalsIgnoreCase("Basic") || 91 info.getScheme().equalsIgnoreCase("SOCKS5")) 92 return info; 93 else if (!info.getScheme().equalsIgnoreCase("Digest")) 94 throw new AuthSchemeNotImplException(info.getScheme()); 95 96 if (DebugAuth) 97 System.err.println("Auth: fixing up Authorization for host " + 98 info.getHost()+":"+info.getPort() + 99 "; scheme: " + info.getScheme() + 100 "; realm: " + info.getRealm()); 101 102 return digest_fixup(info, req, challenge, resp); 103 } 104 105 106 117 public AuthorizationInfo getAuthorization(AuthorizationInfo challenge, 118 RoRequest req, RoResponse resp) 119 throws AuthSchemeNotImplException 120 { 121 AuthorizationInfo cred; 122 123 124 if (DebugAuth) 125 System.err.println("Auth: Requesting Authorization for host " + 126 challenge.getHost()+":"+challenge.getPort() + 127 "; scheme: " + challenge.getScheme() + 128 "; realm: " + challenge.getRealm()); 129 130 131 133 if (!challenge.getScheme().equalsIgnoreCase("Basic") && 134 !challenge.getScheme().equalsIgnoreCase("Digest") && 135 !challenge.getScheme().equalsIgnoreCase("SOCKS5")) 136 throw new AuthSchemeNotImplException(challenge.getScheme()); 137 138 139 141 if (challenge.getScheme().equalsIgnoreCase("Digest")) 142 { 143 cred = digest_check_stale(challenge, req, resp); 144 if (cred != null) 145 return cred; 146 } 147 148 149 151 if (!req.allowUI()) 152 return null; 153 154 if (inp == null) 155 { 156 synchronized(getClass()) 157 { 158 if (inp == null) 159 inp = new BasicAuthBox(); 160 } 161 } 162 163 NVPair answer; 164 if (challenge.getScheme().equalsIgnoreCase("basic") || 165 challenge.getScheme().equalsIgnoreCase("Digest")) 166 { 167 answer = inp.getInput("Enter username and password for realm `" + 168 challenge.getRealm() + "'", 169 "on host " + challenge.getHost() + ":" + 170 challenge.getPort(), 171 "Authentication Scheme: " + 172 challenge.getScheme()); 173 } 174 else 175 { 176 answer = inp.getInput("Enter username and password for SOCKS " + 177 "server on host ", challenge.getHost(), 178 "Authentication Method: username/password"); 179 } 180 181 if (answer == null) 182 return null; 183 184 185 187 if (challenge.getScheme().equalsIgnoreCase("basic")) 188 { 189 cred = new AuthorizationInfo(challenge.getHost(), 190 challenge.getPort(), 191 challenge.getScheme(), 192 challenge.getRealm(), 193 Codecs.base64Encode( 194 answer.getName() + ":" + 195 answer.getValue())); 196 } 197 else if (challenge.getScheme().equalsIgnoreCase("Digest")) 198 { 199 cred = digest_gen_auth_info(challenge.getHost(), 200 challenge.getPort(), 201 challenge.getRealm(), answer.getName(), 202 answer.getValue(), 203 req.getConnection().getContext()); 204 cred = digest_fixup(cred, req, challenge, null); 205 } 206 else { 208 NVPair[] upwd = { answer }; 209 cred = new AuthorizationInfo(challenge.getHost(), 210 challenge.getPort(), 211 challenge.getScheme(), 212 challenge.getRealm(), 213 upwd, null); 214 } 215 216 217 219 answer = null; 220 System.gc(); 221 222 223 225 if (DebugAuth) System.err.println("Auth: Got Authorization"); 226 227 return cred; 228 } 229 230 231 235 public void handleAuthHeaders(Response resp, RoRequest req, 236 AuthorizationInfo prev, 237 AuthorizationInfo prxy) 238 throws IOException 239 { 240 String auth_info = resp.getHeader("Authentication-Info"); 241 String prxy_info = resp.getHeader("Proxy-Authentication-Info"); 242 243 if (auth_info == null && prev != null && 244 hasParam(prev.getParams(), "qop", "auth-int")) 245 auth_info = ""; 246 247 if (prxy_info == null && prxy != null && 248 hasParam(prxy.getParams(), "qop", "auth-int")) 249 prxy_info = ""; 250 251 try 252 { 253 handleAuthInfo(auth_info, "Authentication-Info", prev, resp, req, 254 true); 255 handleAuthInfo(prxy_info, "Proxy-Authentication-Info", prxy, resp, 256 req, true); 257 } 258 catch (ParseException pe) 259 { throw new IOException (pe.toString()); } 260 } 261 262 263 267 public void handleAuthTrailers(Response resp, RoRequest req, 268 AuthorizationInfo prev, 269 AuthorizationInfo prxy) 270 throws IOException 271 { 272 String auth_info = resp.getTrailer("Authentication-Info"); 273 String prxy_info = resp.getTrailer("Proxy-Authentication-Info"); 274 275 try 276 { 277 handleAuthInfo(auth_info, "Authentication-Info", prev, resp, req, 278 false); 279 handleAuthInfo(prxy_info, "Proxy-Authentication-Info", prxy, resp, 280 req, false); 281 } 282 catch (ParseException pe) 283 { throw new IOException (pe.toString()); } 284 } 285 286 287 private static void handleAuthInfo(String auth_info, String hdr_name, 288 AuthorizationInfo prev, Response resp, 289 RoRequest req, boolean in_headers) 290 throws ParseException, IOException 291 { 292 if (auth_info == null) return; 293 294 Vector pai = Util.parseHeader(auth_info); 295 HttpHeaderElement elem; 296 297 if (handle_nextnonce(prev, req, 298 elem = Util.getElement(pai, "nextnonce"))) 299 pai.removeElement(elem); 300 if (handle_discard(prev, req, elem = Util.getElement(pai, "discard"))) 301 pai.removeElement(elem); 302 303 if (in_headers) 304 { 305 HttpHeaderElement qop = null; 306 307 if (pai != null && 308 (qop = Util.getElement(pai, "qop")) != null && 309 qop.getValue() != null) 310 { 311 handle_rspauth(prev, resp, req, pai, hdr_name); 312 } 313 else if (prev != null && 314 (Util.hasToken(resp.getHeader("Trailer"), hdr_name) && 315 hasParam(prev.getParams(), "qop", null) || 316 hasParam(prev.getParams(), "qop", "auth-int"))) 317 { 318 handle_rspauth(prev, resp, req, null, hdr_name); 319 } 320 321 else if ((pai != null && qop == null && 322 pai.contains(new HttpHeaderElement("digest"))) || 323 (Util.hasToken(resp.getHeader("Trailer"), hdr_name) && 324 prev != null && 325 !hasParam(prev.getParams(), "qop", null))) 326 { 327 handle_digest(prev, resp, req, hdr_name); 328 } 329 } 330 331 if (pai.size() > 0) 332 resp.setHeader(hdr_name, Util.assembleHeader(pai)); 333 else 334 resp.deleteHeader(hdr_name); 335 } 336 337 338 private static final boolean hasParam(NVPair[] params, String name, 339 String val) 340 { 341 for (int idx=0; idx<params.length; idx++) 342 if (params[idx].getName().equalsIgnoreCase(name) && 343 (val == null || params[idx].getValue().equalsIgnoreCase(val))) 344 return true; 345 346 return false; 347 } 348 349 350 353 354 private static AuthorizationInfo digest_gen_auth_info(String host, int port, 355 String realm, 356 String user, 357 String pass, 358 Object context) 359 { 360 String A1 = user + ":" + realm + ":" + pass; 361 String [] info = { new MD5(A1).asHex(), null, null }; 362 363 AuthorizationInfo prev = AuthorizationInfo.getAuthorization(host, port, 364 "Digest", realm, context); 365 NVPair[] params; 366 if (prev == null) 367 { 368 params = new NVPair[4]; 369 params[0] = new NVPair("username", user); 370 params[1] = new NVPair("uri", ""); 371 params[2] = new NVPair("nonce", ""); 372 params[3] = new NVPair("response", ""); 373 } 374 else 375 { 376 params = prev.getParams(); 377 for (int idx=0; idx<params.length; idx++) 378 { 379 if (params[idx].getName().equalsIgnoreCase("username")) 380 { 381 params[idx] = new NVPair("username", user); 382 break; 383 } 384 } 385 } 386 387 return new AuthorizationInfo(host, port, "Digest", realm, params, info); 388 } 389 390 391 394 private static AuthorizationInfo digest_fixup(AuthorizationInfo info, 395 RoRequest req, 396 AuthorizationInfo challenge, 397 RoResponse resp) 398 throws AuthSchemeNotImplException 399 { 400 402 int ch_domain=-1, ch_nonce=-1, ch_alg=-1, ch_opaque=-1, ch_stale=-1, 403 ch_dreq=-1, ch_qop=-1; 404 NVPair[] ch_params = null; 405 if (challenge != null) 406 { 407 ch_params = challenge.getParams(); 408 409 for (int idx=0; idx<ch_params.length; idx++) 410 { 411 String name = ch_params[idx].getName().toLowerCase(); 412 if (name.equals("domain")) ch_domain = idx; 413 else if (name.equals("nonce")) ch_nonce = idx; 414 else if (name.equals("opaque")) ch_opaque = idx; 415 else if (name.equals("algorithm")) ch_alg = idx; 416 else if (name.equals("stale")) ch_stale = idx; 417 else if (name.equals("digest-required")) ch_dreq = idx; 418 else if (name.equals("qop")) ch_qop = idx; 419 } 420 } 421 422 423 425 int uri=-1, user=-1, alg=-1, response=-1, nonce=-1, cnonce=-1, nc=-1, 426 opaque=-1, digest=-1, dreq=-1, qop=-1; 427 NVPair[] params; 428 String [] extra; 429 430 synchronized(info) { 432 params = info.getParams(); 433 434 for (int idx=0; idx<params.length; idx++) 435 { 436 String name = params[idx].getName().toLowerCase(); 437 if (name.equals("uri")) uri = idx; 438 else if (name.equals("username")) user = idx; 439 else if (name.equals("algorithm")) alg = idx; 440 else if (name.equals("nonce")) nonce = idx; 441 else if (name.equals("cnonce")) cnonce = idx; 442 else if (name.equals("nc")) nc = idx; 443 else if (name.equals("response")) response = idx; 444 else if (name.equals("opaque")) opaque = idx; 445 else if (name.equals("digest")) digest = idx; 446 else if (name.equals("digest-required")) dreq = idx; 447 else if (name.equals("qop")) qop = idx; 448 } 449 450 extra = (String []) info.getExtraInfo(); 451 452 453 455 if (alg != -1 && 456 !params[alg].getValue().equalsIgnoreCase("MD5") && 457 !params[alg].getValue().equalsIgnoreCase("MD5-sess")) 458 throw new AuthSchemeNotImplException("Digest auth scheme: " + 459 "Algorithm " + params[alg].getValue() + 460 " not implemented"); 461 462 if (ch_alg != -1 && 463 !ch_params[ch_alg].getValue().equalsIgnoreCase("MD5") && 464 !ch_params[ch_alg].getValue().equalsIgnoreCase("MD5-sess")) 465 throw new AuthSchemeNotImplException("Digest auth scheme: " + 466 "Algorithm " + ch_params[ch_alg].getValue()+ 467 " not implemented"); 468 469 470 472 params[uri] = new NVPair("uri", req.getRequestURI()); 473 String old_nonce = params[nonce].getValue(); 474 if (ch_nonce != -1 && 475 !old_nonce.equals(ch_params[ch_nonce].getValue())) 476 params[nonce] = ch_params[ch_nonce]; 477 478 479 482 if (ch_opaque != -1) 483 { 484 if (opaque == -1) 485 { 486 params = Util.resizeArray(params, params.length+1); 487 opaque = params.length-1; 488 } 489 params[opaque] = ch_params[ch_opaque]; 490 } 491 492 if (ch_alg != -1) 493 { 494 if (alg == -1) 495 { 496 params = Util.resizeArray(params, params.length+1); 497 alg = params.length-1; 498 } 499 params[alg] = ch_params[ch_alg]; 500 } 501 502 if (ch_qop != -1 || 503 (ch_alg != -1 && 504 ch_params[ch_alg].getValue().equalsIgnoreCase("MD5-sess"))) 505 { 506 if (cnonce == -1) 507 { 508 params = Util.resizeArray(params, params.length+1); 509 cnonce = params.length-1; 510 } 511 512 if (digest_secret == null) 513 digest_secret = gen_random_bytes(20); 514 515 long l_time = System.currentTimeMillis(); 516 byte[] time = new byte[8]; 517 time[0] = (byte) (l_time & 0xFF); 518 time[1] = (byte) ((l_time >> 8) & 0xFF); 519 time[2] = (byte) ((l_time >> 16) & 0xFF); 520 time[3] = (byte) ((l_time >> 24) & 0xFF); 521 time[4] = (byte) ((l_time >> 32) & 0xFF); 522 time[5] = (byte) ((l_time >> 40) & 0xFF); 523 time[6] = (byte) ((l_time >> 48) & 0xFF); 524 time[7] = (byte) ((l_time >> 56) & 0xFF); 525 526 MD5 hash = new MD5(digest_secret); 527 hash.Update(time); 528 params[cnonce] = new NVPair("cnonce", hash.asHex()); 529 } 530 531 532 534 if (ch_qop != -1) 535 { 536 if (qop == -1) 537 { 538 params = Util.resizeArray(params, params.length+1); 539 qop = params.length-1; 540 } 541 extra[DI_QOP] = ch_params[ch_qop].getValue(); 542 543 544 546 String [] qops = splitList(extra[DI_QOP], ","); 547 String p = null; 548 for (int idx=0; idx<qops.length; idx++) 549 { 550 if (qops[idx].equalsIgnoreCase("auth-int") && 551 (req.getStream() == null || 552 req.getConnection().ServProtVersKnown && 553 req.getConnection().ServerProtocolVersion >= HTTP_1_1)) 554 { 555 p = "auth-int"; 556 break; 557 } 558 if (qops[idx].equalsIgnoreCase("auth")) 559 p = "auth"; 560 } 561 if (p == null) 562 { 563 for (int idx=0; idx<qops.length; idx++) 564 if (qops[idx].equalsIgnoreCase("auth-int")) 565 throw new AuthSchemeNotImplException( 566 "Digest auth scheme: Can't comply with qop " + 567 "option 'auth-int' because an HttpOutputStream " + 568 "is being used and the server doesn't speak " + 569 "HTTP/1.1"); 570 571 throw new AuthSchemeNotImplException("Digest auth scheme: "+ 572 "None of the available qop options '" + 573 ch_params[ch_qop].getValue() + "' implemented"); 574 } 575 params[qop] = new NVPair("qop", p); 576 } 577 578 579 581 if (qop != -1) 582 { 583 593 if (nc == -1) 594 { 595 params = Util.resizeArray(params, params.length+1); 596 nc = params.length-1; 597 params[nc] = new NVPair("nc", "00000001"); 598 } 599 else if (old_nonce.equals(params[nonce].getValue())) 600 { 601 String c = Long.toHexString( 602 Long.parseLong(params[nc].getValue(), 16) + 1); 603 params[nc] = 604 new NVPair("nc", "00000000".substring(c.length()) + c); 605 } 606 else 607 params[nc] = new NVPair("nc", "00000001"); 608 } 609 610 611 613 if (challenge != null && 614 (ch_stale == -1 || 615 !ch_params[ch_stale].getValue().equalsIgnoreCase("true")) && 616 alg != -1 && 617 params[alg].getValue().equalsIgnoreCase("MD5-sess")) 618 { 619 extra[DI_A1S] = new MD5(extra[DI_A1] + ":" + 620 params[nonce].getValue() + ":" + 621 params[cnonce].getValue()).asHex(); 622 } 623 624 625 627 info.setParams(params); 628 info.setExtraInfo(extra); 629 } 630 631 632 634 String hash = null; 635 if (qop != -1 && params[qop].getValue().equalsIgnoreCase("auth-int") && 636 req.getStream() == null) 637 { 638 MD5 entity_hash = new MD5(); 639 entity_hash.Update(req.getData() == null ? NUL : req.getData()); 640 hash = entity_hash.asHex(); 641 } 642 643 if (req.getStream() == null) 644 params[response] = new NVPair("response", 645 calcResponseAttr(hash, extra, params, alg, uri, qop, nonce, 646 nc, cnonce, req.getMethod())); 647 648 649 651 AuthorizationInfo new_info; 652 653 boolean ch_dreq_val = false; 654 if (ch_dreq != -1 && 655 (ch_params[ch_dreq].getValue() == null || 656 ch_params[ch_dreq].getValue().equalsIgnoreCase("true"))) 657 ch_dreq_val = true; 658 659 if ((ch_dreq_val || digest != -1) && req.getStream() == null) 660 { 661 NVPair[] d_params; 662 if (digest == -1) 663 { 664 d_params = Util.resizeArray(params, params.length+1); 665 digest = params.length; 666 } 667 else 668 d_params = params; 669 d_params[digest] = new NVPair("digest", 670 calc_digest(req, extra[DI_A1], params[nonce].getValue())); 671 672 if (dreq == -1) { 674 dreq = d_params.length; 675 d_params = Util.resizeArray(d_params, d_params.length+1); 676 d_params[dreq] = new NVPair("digest-required", "true"); 677 } 678 679 new_info = new AuthorizationInfo(info.getHost(), info.getPort(), 680 info.getScheme(), info.getRealm(), 681 d_params, extra); 682 } 683 else if (ch_dreq_val) 684 new_info = null; 685 else 686 new_info = new AuthorizationInfo(info.getHost(), info.getPort(), 687 info.getScheme(), info.getRealm(), 688 params, extra); 689 690 691 693 boolean from_server = (challenge != null) && 694 challenge.getHost().equalsIgnoreCase(req.getConnection().getHost()); 695 if (ch_domain != -1) 696 { 697 URI base = null; 698 try 699 { 700 base = new URI(req.getConnection().getProtocol(), 701 req.getConnection().getHost(), 702 req.getConnection().getPort(), 703 req.getRequestURI()); 704 } 705 catch (ParseException pe) 706 { } 707 708 StringTokenizer tok = 709 new StringTokenizer (ch_params[ch_domain].getValue()); 710 while (tok.hasMoreTokens()) 711 { 712 URI Uri; 713 try 714 { Uri = new URI(base, tok.nextToken()); } 715 catch (ParseException pe) 716 { continue; } 717 718 AuthorizationInfo tmp = 719 AuthorizationInfo.getAuthorization(Uri.getHost(), 720 Uri.getPort(), 721 info.getScheme(), 722 info.getRealm(), 723 req.getConnection().getContext()); 724 if (tmp == null) 725 { 726 params[uri] = new NVPair("uri", Uri.getPath()); 727 tmp = new AuthorizationInfo(Uri.getHost(), Uri.getPort(), 728 info.getScheme(), 729 info.getRealm(), params, 730 extra); 731 AuthorizationInfo.addAuthorization(tmp); 732 } 733 if (from_server) 734 tmp.addPath(Uri.getPath()); 735 } 736 } 737 else if (from_server && challenge != null) 738 { 739 AuthorizationInfo tmp = 742 AuthorizationInfo.getAuthorization(challenge.getHost(), 743 challenge.getPort(), 744 info.getScheme(), 745 info.getRealm(), 746 req.getConnection().getContext()); 747 if (tmp != null) tmp.addPath("/"); 748 } 749 750 751 753 return new_info; 754 } 755 756 757 760 private static AuthorizationInfo digest_check_stale( 761 AuthorizationInfo challenge, 762 RoRequest req, RoResponse resp) 763 throws AuthSchemeNotImplException 764 { 765 AuthorizationInfo cred = null; 766 767 NVPair[] params = challenge.getParams(); 768 for (int idx=0; idx<params.length; idx++) 769 { 770 String name = params[idx].getName(); 771 if (name.equalsIgnoreCase("stale") && 772 params[idx].getValue().equalsIgnoreCase("true")) 773 { 774 cred = AuthorizationInfo.getAuthorization(challenge, req, resp, 775 false); 776 if (cred != null) return digest_fixup(cred, req, challenge, resp); 778 break; } 780 } 781 782 return cred; 783 } 784 785 786 789 private static boolean handle_nextnonce(AuthorizationInfo prev, 790 RoRequest req, 791 HttpHeaderElement nextnonce) 792 { 793 if (prev == null || nextnonce == null || 794 nextnonce.getValue() == null) 795 return false; 796 797 AuthorizationInfo ai; 798 try 799 { ai = AuthorizationInfo.getAuthorization(prev, req, null, false); } 800 catch (AuthSchemeNotImplException asnie) 801 { ai = prev; } 802 synchronized(ai) 803 { 804 NVPair[] params = ai.getParams(); 805 params = setValue(params, "nonce", nextnonce.getValue()); 806 params = setValue(params, "nc", "00000000"); 807 ai.setParams(params); 808 } 809 810 return true; 811 } 812 813 814 817 private static boolean handle_digest(AuthorizationInfo prev, Response resp, 818 RoRequest req, String hdr_name) 819 throws IOException 820 { 821 if (prev == null) 822 return false; 823 824 NVPair[] params = prev.getParams(); 825 VerifyDigest 826 verifier = new VerifyDigest(((String []) prev.getExtraInfo())[0], 827 getValue(params, "nonce"), 828 req.getMethod(), 829 getValue(params, "uri"), 830 hdr_name, resp); 831 832 if (resp.hasEntity()) 833 { 834 if (DebugAuth) 835 System.err.println("Auth: pushing md5-check-stream to verify "+ 836 "digest from " + hdr_name); 837 resp.inp_stream = new MD5InputStream(resp.inp_stream, verifier); 838 } 839 else 840 { 841 if (DebugAuth) 842 System.err.println("Auth: verifying digest from " + hdr_name); 843 verifier.verifyHash(new MD5().Final(), 0); 844 } 845 846 return true; 847 } 848 849 850 853 private static boolean handle_rspauth(AuthorizationInfo prev, Response resp, 854 RoRequest req, Vector auth_info, 855 String hdr_name) 856 throws IOException 857 { 858 if (prev == null) 859 return false; 860 861 862 864 NVPair[] params = prev.getParams(); 865 int uri=-1, alg=-1, nonce=-1, cnonce=-1, nc=-1; 866 for (int idx=0; idx<params.length; idx++) 867 { 868 String name = params[idx].getName().toLowerCase(); 869 if (name.equals("uri")) uri = idx; 870 else if (name.equals("algorithm")) alg = idx; 871 else if (name.equals("nonce")) nonce = idx; 872 else if (name.equals("cnonce")) cnonce = idx; 873 else if (name.equals("nc")) nc = idx; 874 } 875 876 877 879 VerifyRspAuth 880 verifier = new VerifyRspAuth(params[uri].getValue(), 881 ((String []) prev.getExtraInfo())[0], 882 (alg == -1 ? null : params[alg].getValue()), 883 params[nonce].getValue(), 884 (cnonce == -1 ? "" : params[cnonce].getValue()), 885 (nc == -1 ? "" : params[nc].getValue()), 886 hdr_name, resp); 887 888 889 891 HttpHeaderElement qop = null; 892 if (auth_info != null && 893 (qop = Util.getElement(auth_info, "qop")) != null && 894 qop.getValue() != null && 895 (qop.getValue().equalsIgnoreCase("auth") || 896 !resp.hasEntity() && qop.getValue().equalsIgnoreCase("auth-int")) 897 ) 898 { 899 if (DebugAuth) 900 System.err.println("Auth: verifying rspauth from " + hdr_name); 901 verifier.verifyHash(new MD5().Final(), 0); 902 } 903 else 904 { 905 907 if (DebugAuth) 908 System.err.println("Auth: pushing md5-check-stream to verify "+ 909 "rspauth from " + hdr_name); 910 resp.inp_stream = new MD5InputStream(resp.inp_stream, verifier); 911 } 912 913 return true; 914 } 915 916 917 920 private static String calcResponseAttr(String hash, String [] extra, 921 NVPair[] params, int alg, 922 int uri, int qop, int nonce, 923 int nc, int cnonce, String method) 924 { 925 String A1, A2, resp_val; 926 927 if (alg != -1 && 928 params[alg].getValue().equalsIgnoreCase("MD5-sess")) 929 A1 = extra[DI_A1S]; 930 else 931 A1 = extra[DI_A1]; 932 933 A2 = method + ":" + params[uri].getValue(); 934 if (qop != -1 && 935 params[qop].getValue().equalsIgnoreCase("auth-int")) 936 { 937 A2 += ":" + hash; 938 } 939 A2 = new MD5(A2).asHex(); 940 941 if (qop == -1) 942 resp_val = new MD5(A1 + ":" + params[nonce].getValue() + ":" + 943 A2).asHex(); 944 else 945 resp_val = 946 new MD5(A1 + ":" + params[nonce].getValue() + ":" + 947 params[nc].getValue() + ":" + 948 params[cnonce].getValue() + ":" + 949 params[qop].getValue() + ":" + A2).asHex(); 950 951 return resp_val; 952 } 953 954 955 960 private static String calc_digest(RoRequest req, String A1_hash, 961 String nonce) 962 { 963 if (req.getStream() != null) 964 return ""; 965 966 int ct=-1, ce=-1, lm=-1, ex=-1, dt=-1; 967 for (int idx=0; idx<req.getHeaders().length; idx++) 968 { 969 String name = req.getHeaders()[idx].getName(); 970 if (name.equalsIgnoreCase("Content-type")) 971 ct = idx; 972 else if (name.equalsIgnoreCase("Content-Encoding")) 973 ce = idx; 974 else if (name.equalsIgnoreCase("Last-Modified")) 975 lm = idx; 976 else if (name.equalsIgnoreCase("Expires")) 977 ex = idx; 978 else if (name.equalsIgnoreCase("Date")) 979 dt = idx; 980 } 981 982 983 NVPair[] hdrs = req.getHeaders(); 984 byte[] entity_body = (req.getData() == null ? NUL : req.getData()); 985 MD5 entity_hash = new MD5(); 986 entity_hash.Update(entity_body); 987 988 String entity_info = new MD5(req.getRequestURI() + ":" + 989 (ct == -1 ? "" : hdrs[ct].getValue()) + ":" + 990 entity_body.length + ":" + 991 (ce == -1 ? "" : hdrs[ce].getValue()) + ":" + 992 (lm == -1 ? "" : hdrs[lm].getValue()) + ":" + 993 (ex == -1 ? "" : hdrs[ex].getValue())).asHex(); 994 String entity_digest = A1_hash + ":" + nonce + ":" + req.getMethod() + 995 ":" + (dt == -1 ? "" : hdrs[dt].getValue()) + 996 ":" + entity_info + ":" + entity_hash.asHex(); 997 998 if (DebugAuth) 999 { 1000 System.err.println("Auth: Entity-Info: '" + req.getRequestURI() + ":" + 1001 (ct == -1 ? "" : hdrs[ct].getValue()) + ":" + 1002 entity_body.length + ":" + 1003 (ce == -1 ? "" : hdrs[ce].getValue()) + ":" + 1004 (lm == -1 ? "" : hdrs[lm].getValue()) + ":" + 1005 (ex == -1 ? "" : hdrs[ex].getValue()) +"'"); 1006 System.err.println("Auth: Entity-Body: '" + entity_hash.asHex() + "'"); 1007 System.err.println("Auth: Entity-Digest: '" + entity_digest + "'"); 1008 } 1009 1010 return new MD5(entity_digest).asHex(); 1011 } 1012 1013 1014 1017 private static boolean handle_discard(AuthorizationInfo prev, RoRequest req, 1018 HttpHeaderElement discard) 1019 { 1020 if (discard != null && prev != null) 1021 { 1022 AuthorizationInfo.removeAuthorization(prev, 1023 req.getConnection().getContext()); 1024 return true; 1025 } 1026 1027 return false; 1028 } 1029 1030 1031 1037 private static byte[] gen_random_bytes(int num) 1038 { 1039 try 1041 { 1042 FileInputStream rnd = new FileInputStream ("/dev/random"); 1043 DataInputStream din = new DataInputStream (rnd); 1044 byte[] data = new byte[num]; 1045 din.readFully(data); 1046 try { din.close(); } catch (IOException ioe) { } 1047 return data; 1048 } 1049 catch (Throwable t) 1050 { } 1051 1052 1055 1057 1058 byte[] data = new byte[num]; 1059 try 1060 { 1061 long fm = Runtime.getRuntime().freeMemory(); 1062 data[0] = (byte) (fm & 0xFF); 1063 data[1] = (byte) ((fm >> 8) & 0xFF); 1064 1065 int h = data.hashCode(); 1066 data[2] = (byte) (h & 0xFF); 1067 data[3] = (byte) ((h >> 8) & 0xFF); 1068 data[4] = (byte) ((h >> 16) & 0xFF); 1069 data[5] = (byte) ((h >> 24) & 0xFF); 1070 1071 long time = System.currentTimeMillis(); 1072 data[6] = (byte) (time & 0xFF); 1073 data[7] = (byte) ((time >> 8) & 0xFF); 1074 } 1075 catch (ArrayIndexOutOfBoundsException aioobe) 1076 { } 1077 1078 return data; 1079 } 1080 1081 1082 1091 private final static String getValue(NVPair[] list, String key) 1092 { 1093 int len = list.length; 1094 1095 for (int idx=0; idx<len; idx++) 1096 if (list[idx].getName().equalsIgnoreCase(key)) 1097 return list[idx].getValue(); 1098 1099 return null; 1100 } 1101 1102 1111 private final static int getIndex(NVPair[] list, String key) 1112 { 1113 int len = list.length; 1114 1115 for (int idx=0; idx<len; idx++) 1116 if (list[idx].getName().equalsIgnoreCase(key)) 1117 return idx; 1118 1119 return -1; 1120 } 1121 1122 1131 private final static NVPair[] setValue(NVPair[] list, String key, String val) 1132 { 1133 int idx = getIndex(list, key); 1134 if (idx == -1) 1135 { 1136 idx = list.length; 1137 list = Util.resizeArray(list, list.length+1); 1138 } 1139 1140 list[idx] = new NVPair(key, val); 1141 return list; 1142 } 1143 1144 1145 1149 private static String [] splitList(String str, String sep) 1150 { 1151 if (str == null) return new String [0]; 1152 1153 StringTokenizer tok = new StringTokenizer (str, sep); 1154 String [] list = new String [tok.countTokens()]; 1155 for (int idx=0; idx<list.length; idx++) 1156 list[idx] = tok.nextToken().trim(); 1157 1158 return list; 1159 } 1160 1161 1162 1165 static String hex(byte[] buf) 1166 { 1167 StringBuffer str = new StringBuffer (buf.length*3); 1168 for (int idx=0; idx<buf.length; idx++) 1169 { 1170 str.append(Character.forDigit(buf[idx] >>> 4, 16)); 1171 str.append(Character.forDigit(buf[idx] & 16, 16)); 1172 str.append(':'); 1173 } 1174 str.setLength(str.length()-1); 1175 1176 return str.toString(); 1177 } 1178 1179 1180 static final byte[] unHex(String hex) 1181 { 1182 byte[] digest = new byte[hex.length()/2]; 1183 1184 for (int idx=0; idx<digest.length; idx++) 1185 { 1186 digest[idx] = (byte) (0xFF & Integer.parseInt( 1187 hex.substring(2*idx, 2*(idx+1)), 16)); 1188 } 1189 1190 return digest; 1191 } 1192} 1193 1194 1195 1198class VerifyRspAuth implements HashVerifier, GlobalConstants 1199{ 1200 private String uri; 1201 private String HA1; 1202 private String alg; 1203 private String nonce; 1204 private String cnonce; 1205 private String nc; 1206 private String hdr; 1207 private RoResponse resp; 1208 1209 1210 public VerifyRspAuth(String uri, String HA1, String alg, String nonce, 1211 String cnonce, String nc, String hdr, RoResponse resp) 1212 { 1213 this.uri = uri; 1214 this.HA1 = HA1; 1215 this.alg = alg; 1216 this.nonce = nonce; 1217 this.cnonce = cnonce; 1218 this.nc = nc; 1219 this.hdr = hdr; 1220 this.resp = resp; 1221 } 1222 1223 1224 public void verifyHash(byte[] hash, long len) throws IOException 1225 { 1226 String auth_info = resp.getHeader(hdr); 1227 if (auth_info == null) 1228 auth_info = resp.getTrailer(hdr); 1229 if (auth_info == null) 1230 return; 1231 1232 Vector pai; 1233 try 1234 { pai = Util.parseHeader(auth_info); } 1235 catch (ParseException pe) 1236 { throw new IOException (pe.toString()); } 1237 1238 String qop; 1239 HttpHeaderElement elem = Util.getElement(pai, "qop"); 1240 if (elem == null || (qop = elem.getValue()) == null || 1241 (!qop.equalsIgnoreCase("auth") && 1242 !qop.equalsIgnoreCase("auth-int"))) 1243 return; 1244 1245 elem = Util.getElement(pai, "rspauth"); 1246 if (elem == null || elem.getValue() == null) return; 1247 byte[] digest = DefaultAuthHandler.unHex(elem.getValue()); 1248 1249 elem = Util.getElement(pai, "cnonce"); 1250 if (elem != null && elem.getValue() != null && 1251 !elem.getValue().equals(cnonce)) 1252 throw new IOException ("Digest auth scheme: received wrong " + 1253 "client-nonce '" + elem.getValue() + 1254 "' - expected '" + cnonce + "'"); 1255 1256 elem = Util.getElement(pai, "nc"); 1257 if (elem != null && elem.getValue() != null && 1258 !elem.getValue().equals(nc)) 1259 throw new IOException ("Digest auth scheme: received wrong " + 1260 "nonce-count '" + elem.getValue() + 1261 "' - expected '" + nc + "'"); 1262 1263 String A1, A2; 1264 if (alg != null && alg.equalsIgnoreCase("MD5-sess")) 1265 A1 = new MD5(HA1 + ":" + nonce + ":" + cnonce).asHex(); 1266 else 1267 A1 = HA1; 1268 1269 A2 = ":" + uri; 1271 if (qop.equalsIgnoreCase("auth-int")) 1272 A2 += ":" + MD5.asHex(hash); 1273 A2 = new MD5(A2).asHex(); 1274 1275 hash = new MD5(A1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + 1276 qop + ":" + A2).Final(); 1277 1278 for (int idx=0; idx<hash.length; idx++) 1279 { 1280 if (hash[idx] != digest[idx]) 1281 throw new IOException ("MD5-Digest mismatch: expected " + 1282 DefaultAuthHandler.hex(digest) + 1283 " but calculated " + 1284 DefaultAuthHandler.hex(hash)); 1285 } 1286 1287 if (DebugAuth) 1288 System.err.println("Auth: rspauth from " + hdr + 1289 " successfully verified"); 1290 } 1291} 1292 1293 1294 1297class VerifyDigest implements HashVerifier, GlobalConstants 1298{ 1299 private String HA1; 1300 private String nonce; 1301 private String method; 1302 private String uri; 1303 private String hdr; 1304 private RoResponse resp; 1305 1306 1307 public VerifyDigest(String HA1, String nonce, String method, String uri, 1308 String hdr, RoResponse resp) 1309 { 1310 this.HA1 = HA1; 1311 this.nonce = nonce; 1312 this.method = method; 1313 this.uri = uri; 1314 this.hdr = hdr; 1315 this.resp = resp; 1316 } 1317 1318 1319 public void verifyHash(byte[] hash, long len) throws IOException 1320 { 1321 String auth_info = resp.getHeader(hdr); 1322 if (auth_info == null) 1323 auth_info = resp.getTrailer(hdr); 1324 if (auth_info == null) 1325 return; 1326 1327 Vector pai; 1328 try 1329 { pai = Util.parseHeader(auth_info); } 1330 catch (ParseException pe) 1331 { throw new IOException (pe.toString()); } 1332 HttpHeaderElement elem = Util.getElement(pai, "digest"); 1333 if (elem == null || elem.getValue() == null) 1334 return; 1335 1336 byte[] digest = DefaultAuthHandler.unHex(elem.getValue()); 1337 1338 String entity_info = new MD5( 1339 uri + ":" + 1340 header_val("Content-Type", resp) + ":" + 1341 header_val("Content-Length", resp) + ":" + 1342 header_val("Content-Encoding", resp) + ":" + 1343 header_val("Last-Modified", resp) + ":" + 1344 header_val("Expires", resp)).asHex(); 1345 hash = new MD5(HA1 + ":" + nonce + ":" + method + ":" + 1346 header_val("Date", resp) + 1347 ":" + entity_info + ":" + MD5.asHex(hash)).Final(); 1348 1349 for (int idx=0; idx<hash.length; idx++) 1350 { 1351 if (hash[idx] != digest[idx]) 1352 throw new IOException ("MD5-Digest mismatch: expected " + 1353 DefaultAuthHandler.hex(digest) + 1354 " but calculated " + 1355 DefaultAuthHandler.hex(hash)); 1356 } 1357 1358 if (DebugAuth) 1359 System.err.println("Auth: digest from " + hdr + 1360 " successfully verified"); 1361 } 1362 1363 1364 private static final String header_val(String hdr_name, RoResponse resp) 1365 throws IOException 1366 { 1367 String hdr = resp.getHeader(hdr_name); 1368 String tlr = resp.getTrailer(hdr_name); 1369 return (hdr != null ? hdr : (tlr != null ? tlr : "")); 1370 } 1371} 1372 1373 1374 1381class BasicAuthBox extends Frame 1382{ 1383 private final static String title = "Authorization Request"; 1384 private Dimension screen; 1385 private Label line1, line2, line3; 1386 private TextField user, pass; 1387 private int done; 1388 private final static int OK = 1, CANCEL = 0; 1389 1390 1391 1394 BasicAuthBox() 1395 { 1396 super(title); 1397 1398 screen = getToolkit().getScreenSize(); 1399 1400 addNotify(); 1401 addWindowListener(new Close()); 1402 setLayout(new BorderLayout ()); 1403 1404 Panel p = new Panel (new GridLayout (3,1)); 1405 p.add(line1 = new Label ()); 1406 p.add(line2 = new Label ()); 1407 p.add(line3 = new Label ()); 1408 add("North", p); 1409 1410 p = new Panel (new GridLayout (2,1)); 1411 p.add(new Label ("Username:")); 1412 p.add(new Label ("Password:")); 1413 add("West", p); 1414 p = new Panel (new GridLayout (2,1)); 1415 p.add(user = new TextField (30)); 1416 p.add(pass = new TextField (30)); 1417 pass.addActionListener(new Ok()); 1418 pass.setEchoChar('*'); 1419 add("East", p); 1420 1421 GridBagLayout gb = new GridBagLayout (); 1422 p = new Panel (gb); 1423 GridBagConstraints constr = new GridBagConstraints (); 1424 Panel pp = new Panel (); 1425 p.add(pp); 1426 constr.gridwidth = GridBagConstraints.REMAINDER; 1427 gb.setConstraints(pp, constr); 1428 constr.gridwidth = 1; 1429 constr.weightx = 1.0; 1430 Button b; 1431 p.add(b = new Button (" OK ")); 1432 b.addActionListener(new Ok()); 1433 constr.weightx = 1.0; 1434 gb.setConstraints(b, constr); 1435 p.add(b = new Button ("Clear")); 1436 b.addActionListener(new Clear()); 1437 constr.weightx = 2.0; 1438 gb.setConstraints(b, constr); 1439 p.add(b = new Button ("Cancel")); 1440 b.addActionListener(new Cancel()); 1441 constr.weightx = 1.0; 1442 gb.setConstraints(b, constr); 1443 add("South", p); 1444 1445 pack(); 1446 setResizable(false); 1447 } 1448 1449 1450 1453 private class Ok implements ActionListener 1454 { 1455 public void actionPerformed(ActionEvent ae) 1456 { 1457 done = OK; 1458 synchronized (BasicAuthBox.this) { BasicAuthBox.this.notifyAll(); } 1459 } 1460 } 1461 1462 private class Clear implements ActionListener 1463 { 1464 public void actionPerformed(ActionEvent ae) 1465 { 1466 user.setText(""); 1467 pass.setText(""); 1468 user.requestFocus(); 1469 } 1470 } 1471 1472 private class Cancel implements ActionListener 1473 { 1474 public void actionPerformed(ActionEvent ae) 1475 { 1476 done = CANCEL; 1477 synchronized (BasicAuthBox.this) { BasicAuthBox.this.notifyAll(); } 1478 } 1479 } 1480 1481 1482 private class Close extends WindowAdapter 1483 { 1484 public void windowClosing(WindowEvent we) 1485 { 1486 new Cancel().actionPerformed(null); 1487 } 1488 } 1489 1490 1491 1496 synchronized NVPair getInput(String l1, String l2, String l3) 1497 { 1498 line1.setText(l1); 1499 line2.setText(l2); 1500 line3.setText(l3); 1501 1502 line1.invalidate(); 1503 line2.invalidate(); 1504 line3.invalidate(); 1505 1506 setResizable(true); 1507 pack(); 1508 setResizable(false); 1509 setLocation((screen.width-getPreferredSize().width)/2, 1510 (int) ((screen.height-getPreferredSize().height)/2*.7)); 1511 user.requestFocus(); 1512 setVisible(true); 1513 1514 try { wait(); } catch (InterruptedException e) { } 1515 1516 setVisible(false); 1517 1518 NVPair result = new NVPair(user.getText(), pass.getText()); 1519 user.setText(""); 1520 pass.setText(""); 1521 1522 if (done == CANCEL) 1523 return null; 1524 else 1525 return result; 1526 } 1527} 1528 1529 | Popular Tags |