| 1 17 18 19 package org.apache.catalina.realm; 20 21 22 import java.beans.PropertyChangeListener ; 23 import java.beans.PropertyChangeSupport ; 24 import java.io.IOException ; 25 import java.io.UnsupportedEncodingException ; 26 import java.security.MessageDigest ; 27 import java.security.NoSuchAlgorithmException ; 28 import java.security.Principal ; 29 import java.security.cert.X509Certificate ; 30 import java.util.ArrayList ; 31 32 import javax.management.Attribute ; 33 import javax.management.MBeanRegistration ; 34 import javax.management.MBeanServer ; 35 import javax.management.ObjectName ; 36 import javax.servlet.http.HttpServletResponse ; 37 38 import org.apache.catalina.Container; 39 import org.apache.catalina.Context; 40 import org.apache.catalina.Lifecycle; 41 import org.apache.catalina.LifecycleException; 42 import org.apache.catalina.LifecycleListener; 43 import org.apache.catalina.Realm; 44 import org.apache.catalina.connector.Request; 45 import org.apache.catalina.connector.Response; 46 import org.apache.catalina.core.ContainerBase; 47 import org.apache.catalina.deploy.LoginConfig; 48 import org.apache.catalina.deploy.SecurityConstraint; 49 import org.apache.catalina.deploy.SecurityCollection; 50 import org.apache.catalina.util.HexUtils; 51 import org.apache.catalina.util.LifecycleSupport; 52 import org.apache.catalina.util.MD5Encoder; 53 import org.apache.catalina.util.StringManager; 54 import org.apache.commons.logging.Log; 55 import org.apache.commons.logging.LogFactory; 56 import org.apache.tomcat.util.modeler.Registry; 57 58 66 67 public abstract class RealmBase 68 implements Lifecycle, Realm, MBeanRegistration { 69 70 private static Log log = LogFactory.getLog(RealmBase.class); 71 72 74 75 78 protected Container container = null; 79 80 81 84 protected Log containerLog = null; 85 86 87 93 protected String digest = null; 94 95 98 protected String digestEncoding = null; 99 100 101 104 protected static final String info = 105 "org.apache.catalina.realm.RealmBase/1.0"; 106 107 108 111 protected LifecycleSupport lifecycle = new LifecycleSupport(this); 112 113 114 117 protected MessageDigest md = null; 118 119 120 123 protected static final MD5Encoder md5Encoder = new MD5Encoder(); 124 125 126 129 protected static MessageDigest md5Helper; 130 131 132 135 protected static StringManager sm = 136 StringManager.getManager(Constants.Package); 137 138 139 142 protected boolean started = false; 143 144 145 148 protected PropertyChangeSupport support = new PropertyChangeSupport (this); 149 150 151 154 protected boolean validate = true; 155 156 157 160 protected AllRolesMode allRolesMode = AllRolesMode.STRICT_MODE; 161 162 163 165 166 169 public Container getContainer() { 170 171 return (container); 172 173 } 174 175 176 181 public void setContainer(Container container) { 182 183 Container oldContainer = this.container; 184 this.container = container; 185 support.firePropertyChange("container", oldContainer, this.container); 186 187 } 188 189 192 public String getAllRolesMode() { 193 194 return allRolesMode.toString(); 195 196 } 197 198 199 202 public void setAllRolesMode(String allRolesMode) { 203 204 this.allRolesMode = AllRolesMode.toMode(allRolesMode); 205 206 } 207 208 211 public String getDigest() { 212 213 return digest; 214 215 } 216 217 218 223 public void setDigest(String digest) { 224 225 this.digest = digest; 226 227 } 228 229 234 public String getDigestEncoding() { 235 return digestEncoding; 236 } 237 238 243 public void setDigestEncoding(String charset) { 244 digestEncoding = charset; 245 } 246 247 252 public String getInfo() { 253 254 return info; 255 256 } 257 258 259 262 public boolean getValidate() { 263 264 return (this.validate); 265 266 } 267 268 269 274 public void setValidate(boolean validate) { 275 276 this.validate = validate; 277 278 } 279 280 281 283 284 285 290 public void addPropertyChangeListener(PropertyChangeListener listener) { 291 292 support.addPropertyChangeListener(listener); 293 294 } 295 296 297 305 public Principal authenticate(String username, String credentials) { 306 307 String serverCredentials = getPassword(username); 308 309 boolean validated ; 310 if ( serverCredentials == null ) { 311 validated = false; 312 } else if(hasMessageDigest()) { 313 validated = serverCredentials.equalsIgnoreCase(digest(credentials)); 314 } else { 315 validated = serverCredentials.equals(credentials); 316 } 317 if(! validated ) { 318 if (containerLog.isTraceEnabled()) { 319 containerLog.trace(sm.getString("realmBase.authenticateFailure", 320 username)); 321 } 322 return null; 323 } 324 if (containerLog.isTraceEnabled()) { 325 containerLog.trace(sm.getString("realmBase.authenticateSuccess", 326 username)); 327 } 328 329 return getPrincipal(username); 330 } 331 332 333 341 public Principal authenticate(String username, byte[] credentials) { 342 343 return (authenticate(username, credentials.toString())); 344 345 } 346 347 348 361 public Principal authenticate(String username, String clientDigest, 362 String nOnce, String nc, String cnonce, 363 String qop, String realm, 364 String md5a2) { 365 366 String md5a1 = getDigest(username, realm); 367 if (md5a1 == null) 368 return null; 369 String serverDigestValue = md5a1 + ":" + nOnce + ":" + nc + ":" 370 + cnonce + ":" + qop + ":" + md5a2; 371 372 byte[] valueBytes = null; 373 if(getDigestEncoding() == null) { 374 valueBytes = serverDigestValue.getBytes(); 375 } else { 376 try { 377 valueBytes = serverDigestValue.getBytes(getDigestEncoding()); 378 } catch (UnsupportedEncodingException uee) { 379 log.error("Illegal digestEncoding: " + getDigestEncoding(), uee); 380 throw new IllegalArgumentException (uee.getMessage()); 381 } 382 } 383 384 String serverDigest = null; 385 synchronized(md5Helper) { 387 serverDigest = md5Encoder.encode(md5Helper.digest(valueBytes)); 388 } 389 390 if (log.isDebugEnabled()) { 391 log.debug("Digest : " + clientDigest + " Username:" + username 392 + " ClientSigest:" + clientDigest + " nOnce:" + nOnce 393 + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop 394 + " realm:" + realm + "md5a2:" + md5a2 395 + " Server digest:" + serverDigest); 396 } 397 398 if (serverDigest.equals(clientDigest)) 399 return getPrincipal(username); 400 else 401 return null; 402 } 403 404 405 406 413 public Principal authenticate(X509Certificate certs[]) { 414 415 if ((certs == null) || (certs.length < 1)) 416 return (null); 417 418 if (log.isDebugEnabled()) 420 log.debug("Authenticating client certificate chain"); 421 if (validate) { 422 for (int i = 0; i < certs.length; i++) { 423 if (log.isDebugEnabled()) 424 log.debug(" Checking validity for '" + 425 certs[i].getSubjectDN().getName() + "'"); 426 try { 427 certs[i].checkValidity(); 428 } catch (Exception e) { 429 if (log.isDebugEnabled()) 430 log.debug(" Validity exception", e); 431 return (null); 432 } 433 } 434 } 435 436 return (getPrincipal(certs[0])); 438 439 } 440 441 442 447 public void backgroundProcess() { 448 } 449 450 451 458 public SecurityConstraint [] findSecurityConstraints(Request request, 459 Context context) { 460 461 ArrayList results = null; 462 SecurityConstraint constraints[] = context.findConstraints(); 464 if ((constraints == null) || (constraints.length == 0)) { 465 if (log.isDebugEnabled()) 466 log.debug(" No applicable constraints defined"); 467 return (null); 468 } 469 470 String uri = request.getRequestPathMB().toString(); 472 473 String method = request.getMethod(); 474 int i; 475 boolean found = false; 476 for (i = 0; i < constraints.length; i++) { 477 SecurityCollection [] collection = constraints[i].findCollections(); 478 479 if ( collection == null) { 482 continue; 483 } 484 485 if (log.isDebugEnabled()) { 486 log.debug(" Checking constraint '" + constraints[i] + 487 "' against " + method + " " + uri + " --> " + 488 constraints[i].included(uri, method)); 489 } 490 491 for(int j=0; j < collection.length; j++){ 492 String [] patterns = collection[j].findPatterns(); 493 494 if ( patterns == null) { 497 continue; 498 } 499 500 for(int k=0; k < patterns.length; k++) { 501 if(uri.equals(patterns[k])) { 502 found = true; 503 if(collection[j].findMethod(method)) { 504 if(results == null) { 505 results = new ArrayList (); 506 } 507 results.add(constraints[i]); 508 } 509 } 510 } 511 } 512 } 513 514 if(found) { 515 return resultsToArray(results); 516 } 517 518 int longest = -1; 519 520 for (i = 0; i < constraints.length; i++) { 521 SecurityCollection [] collection = constraints[i].findCollections(); 522 523 if ( collection == null) { 526 continue; 527 } 528 529 if (log.isDebugEnabled()) { 530 log.debug(" Checking constraint '" + constraints[i] + 531 "' against " + method + " " + uri + " --> " + 532 constraints[i].included(uri, method)); 533 } 534 535 for(int j=0; j < collection.length; j++){ 536 String [] patterns = collection[j].findPatterns(); 537 538 if ( patterns == null) { 541 continue; 542 } 543 544 boolean matched = false; 545 int length = -1; 546 for(int k=0; k < patterns.length; k++) { 547 String pattern = patterns[k]; 548 if(pattern.startsWith("/") && pattern.endsWith("/*") && 549 pattern.length() >= longest) { 550 551 if(pattern.length() == 2) { 552 matched = true; 553 length = pattern.length(); 554 } else if(pattern.regionMatches(0,uri,0, 555 pattern.length()-1) || 556 (pattern.length()-2 == uri.length() && 557 pattern.regionMatches(0,uri,0, 558 pattern.length()-2))) { 559 matched = true; 560 length = pattern.length(); 561 } 562 } 563 } 564 if(matched) { 565 found = true; 566 if(length > longest) { 567 if(results != null) { 568 results.clear(); 569 } 570 longest = length; 571 } 572 if(collection[j].findMethod(method)) { 573 if(results == null) { 574 results = new ArrayList (); 575 } 576 results.add(constraints[i]); 577 } 578 } 579 } 580 } 581 582 if(found) { 583 return resultsToArray(results); 584 } 585 586 for (i = 0; i < constraints.length; i++) { 587 SecurityCollection [] collection = constraints[i].findCollections(); 588 589 if ( collection == null) { 592 continue; 593 } 594 595 if (log.isDebugEnabled()) { 596 log.debug(" Checking constraint '" + constraints[i] + 597 "' against " + method + " " + uri + " --> " + 598 constraints[i].included(uri, method)); 599 } 600 601 boolean matched = false; 602 int pos = -1; 603 for(int j=0; j < collection.length; j++){ 604 String [] patterns = collection[j].findPatterns(); 605 606 if ( patterns == null) { 609 continue; 610 } 611 612 for(int k=0; k < patterns.length && !matched; k++) { 613 String pattern = patterns[k]; 614 if(pattern.startsWith("*.")){ 615 int slash = uri.lastIndexOf("/"); 616 int dot = uri.lastIndexOf("."); 617 if(slash >= 0 && dot > slash && 618 dot != uri.length()-1 && 619 uri.length()-dot == pattern.length()-1) { 620 if(pattern.regionMatches(1,uri,dot,uri.length()-dot)) { 621 matched = true; 622 pos = j; 623 } 624 } 625 } 626 } 627 } 628 if(matched) { 629 found = true; 630 if(collection[pos].findMethod(method)) { 631 if(results == null) { 632 results = new ArrayList (); 633 } 634 results.add(constraints[i]); 635 } 636 } 637 } 638 639 if(found) { 640 return resultsToArray(results); 641 } 642 643 for (i = 0; i < constraints.length; i++) { 644 SecurityCollection [] collection = constraints[i].findCollections(); 645 646 if ( collection == null) { 649 continue; 650 } 651 652 if (log.isDebugEnabled()) { 653 log.debug(" Checking constraint '" + constraints[i] + 654 "' against " + method + " " + uri + " --> " + 655 constraints[i].included(uri, method)); 656 } 657 658 for(int j=0; j < collection.length; j++){ 659 String [] patterns = collection[j].findPatterns(); 660 661 if ( patterns == null) { 664 continue; 665 } 666 667 boolean matched = false; 668 for(int k=0; k < patterns.length && !matched; k++) { 669 String pattern = patterns[k]; 670 if(pattern.equals("/")){ 671 matched = true; 672 } 673 } 674 if(matched) { 675 if(results == null) { 676 results = new ArrayList (); 677 } 678 results.add(constraints[i]); 679 } 680 } 681 } 682 683 if(results == null) { 684 if (log.isDebugEnabled()) 686 log.debug(" No applicable constraint located"); 687 } 688 return resultsToArray(results); 689 } 690 691 694 private SecurityConstraint [] resultsToArray(ArrayList results) { 695 if(results == null) { 696 return null; 697 } 698 SecurityConstraint [] array = new SecurityConstraint[results.size()]; 699 results.toArray(array); 700 return array; 701 } 702 703 704 716 public boolean hasResourcePermission(Request request, 717 Response response, 718 SecurityConstraint []constraints, 719 Context context) 720 throws IOException { 721 722 if (constraints == null || constraints.length == 0) 723 return (true); 724 725 LoginConfig config = context.getLoginConfig(); 728 if ((config != null) && 729 (Constants.FORM_METHOD.equals(config.getAuthMethod()))) { 730 String requestURI = request.getRequestPathMB().toString(); 731 String loginPage = config.getLoginPage(); 732 if (loginPage.equals(requestURI)) { 733 if (log.isDebugEnabled()) 734 log.debug(" Allow access to login page " + loginPage); 735 return (true); 736 } 737 String errorPage = config.getErrorPage(); 738 if (errorPage.equals(requestURI)) { 739 if (log.isDebugEnabled()) 740 log.debug(" Allow access to error page " + errorPage); 741 return (true); 742 } 743 if (requestURI.endsWith(Constants.FORM_ACTION)) { 744 if (log.isDebugEnabled()) 745 log.debug(" Allow access to username/password submission"); 746 return (true); 747 } 748 } 749 750 Principal principal = request.getPrincipal(); 752 boolean status = false; 753 boolean denyfromall = false; 754 for(int i=0; i < constraints.length; i++) { 755 SecurityConstraint constraint = constraints[i]; 756 757 String roles[]; 758 if (constraint.getAllRoles()) { 759 roles = request.getContext().findSecurityRoles(); 761 } else { 762 roles = constraint.findAuthRoles(); 763 } 764 765 if (roles == null) 766 roles = new String [0]; 767 768 if (log.isDebugEnabled()) 769 log.debug(" Checking roles " + principal); 770 771 if (roles.length == 0 && !constraint.getAllRoles()) { 772 if(constraint.getAuthConstraint()) { 773 if( log.isDebugEnabled() ) 774 log.debug("No roles "); 775 status = false; denyfromall = true; 777 } else { 778 if(log.isDebugEnabled()) 779 log.debug("Passing all access"); 780 return (true); 781 } 782 } else if (principal == null) { 783 if (log.isDebugEnabled()) 784 log.debug(" No user authenticated, cannot grant access"); 785 status = false; 786 } else if(!denyfromall) { 787 788 for (int j = 0; j < roles.length; j++) { 789 if (hasRole(principal, roles[j])) 790 status = true; 791 if( log.isDebugEnabled() ) 792 log.debug( "No role found: " + roles[j]); 793 } 794 } 795 } 796 797 if (allRolesMode != AllRolesMode.STRICT_MODE && !status && principal != null) { 798 if (log.isDebugEnabled()) { 799 log.debug("Checking for all roles mode: " + allRolesMode); 800 } 801 for (int i = 0; i < constraints.length; i++) { 803 SecurityConstraint constraint = constraints[i]; 804 String roles[]; 805 if (constraint.getAllRoles()) { 807 if (allRolesMode == AllRolesMode.AUTH_ONLY_MODE) { 808 if (log.isDebugEnabled()) { 809 log.debug("Granting access for role-name=*, auth-only"); 810 } 811 |