1 22 package org.jboss.web.tomcat.security; 23 24 import java.io.IOException ; 25 import java.lang.reflect.Method ; 26 import java.security.Policy ; 27 import java.security.Principal ; 28 import java.security.cert.X509Certificate ; 29 import java.util.ArrayList ; 30 import java.util.Collections ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.Set ; 37 38 import javax.naming.Context ; 39 import javax.naming.InitialContext ; 40 import javax.naming.NamingException ; 41 import javax.security.auth.Subject ; 42 import javax.security.jacc.PolicyContext ; 43 import javax.security.jacc.PolicyContextException ; 44 import javax.servlet.http.HttpServletRequest ; 45 import javax.servlet.http.HttpServletResponse ; 46 47 import org.apache.catalina.Wrapper; 48 import org.apache.catalina.connector.Request; 49 import org.apache.catalina.connector.Response; 50 import org.apache.catalina.deploy.SecurityConstraint; 51 import org.apache.catalina.realm.GenericPrincipal; 52 import org.apache.catalina.realm.RealmBase; 53 import org.jboss.logging.Logger; 54 import org.jboss.metadata.SecurityRoleRefMetaData; 55 import org.jboss.metadata.WebMetaData; 56 import org.jboss.security.AuthorizationManager; 57 import org.jboss.security.CertificatePrincipal; 58 import org.jboss.security.RealmMapping; 59 import org.jboss.security.SecurityConstants; 60 import org.jboss.security.SecurityContext; 61 import org.jboss.security.SimplePrincipal; 62 import org.jboss.security.SubjectSecurityManager; 63 import org.jboss.security.SecurityContext.SubjectInfo; 64 import org.jboss.security.audit.AuditContext; 65 import org.jboss.security.audit.AuditEvent; 66 import org.jboss.security.audit.AuditLevel; 67 import org.jboss.security.audit.AuditManager; 68 import org.jboss.security.auth.callback.CallbackHandlerPolicyContextHandler; 69 import org.jboss.security.auth.certs.SubjectDNMapping; 70 import org.jboss.security.authorization.AuthorizationContext; 71 import org.jboss.security.authorization.AuthorizationException; 72 import org.jboss.security.authorization.ResourceKeys; 73 import org.jboss.security.plugins.JBossSecurityContext; 74 import org.jboss.web.tomcat.security.authorization.WebResource; 75 76 78 88 public class JBossWebRealm extends RealmBase 89 { 90 static Logger log = Logger.getLogger(JBossWebRealm.class); 91 94 protected CertificatePrincipal certMapping = new SubjectDNMapping(); 95 98 private boolean trace = log.isTraceEnabled(); 99 100 101 private static ThreadLocal activeRequest = new ThreadLocal (); 102 103 104 private static final String SUBJECT_CONTEXT_KEY = "javax.security.auth.Subject.container"; 105 106 protected String securityDomain = SecurityConstants.DEFAULT_WEB_APPLICATION_POLICY; 107 108 111 protected boolean unprotectedResourceDelegation = false; 112 protected String securityConstraintProviderClass = ""; 113 114 115 protected boolean enableAudit = true; 116 117 120 protected boolean ignoreBaseDecision = false; 121 122 130 public void setCertificatePrincipal(String className) 131 { 132 try 133 { 134 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 135 Class cpClass = loader.loadClass(className); 136 certMapping = (CertificatePrincipal) cpClass.newInstance(); 137 } 138 catch (Exception e) 139 { 140 log.error("Failed to load CertificatePrincipal: " + className, e); 141 certMapping = new SubjectDNMapping(); 142 } 143 } 144 145 public void setSecurityConstraintProviderClass(String securityConstraintProviderClass) 146 { 147 this.securityConstraintProviderClass = securityConstraintProviderClass; 148 } 149 150 154 public void setSecurityDomain(String securityDomain) 155 { 156 this.securityDomain = securityDomain; 157 } 158 159 public void setUnprotectedResourceDelegation(boolean unprotectedResourceDelegation) 160 { 161 this.unprotectedResourceDelegation = unprotectedResourceDelegation; 162 } 163 164 public void setEnableAudit(boolean enableAudit) 165 { 166 this.enableAudit = enableAudit; 167 } 168 169 public void setIgnoreBaseDecision(boolean ignoreBaseDecision) 170 { 171 this.ignoreBaseDecision = ignoreBaseDecision; 172 } 173 174 178 179 186 public Principal authenticate(X509Certificate [] certs) 187 { 188 Principal principal = null; 189 Context securityCtx = getSecurityContext(); 190 if (securityCtx == null) 191 { 192 if (trace) 193 { 194 log.trace("No security context for authenticate(X509Certificate[])"); 195 } 196 return null; 197 } 198 199 try 200 { 201 SubjectSecurityManager securityMgr = (SubjectSecurityManager) securityCtx.lookup("securityMgr"); 203 Subject subject = new Subject (); 204 principal = certMapping.toPrinicipal(certs); 205 if (securityMgr.isValid(principal, certs, subject)) 206 { 207 if (trace) 208 { 209 log.trace("User: " + principal + " is authenticated"); 210 } 211 SecurityAssociationActions.setPrincipalInfo(principal, certs, subject); 212 213 securityDomain = securityMgr.getSecurityDomain(); 214 this.establishSecurityContext(securityDomain, 216 principal, certs, subject); 217 218 RealmMapping realmMapping = (RealmMapping) securityCtx.lookup("realmMapping"); 220 Principal oldPrincipal = principal; 221 principal = realmMapping.getPrincipal(oldPrincipal); 222 if (trace) 223 { 224 log.trace("Mapped from input principal: " + oldPrincipal 225 + "to: " + principal); 226 } 227 principal = getCachingPrincpal(realmMapping, oldPrincipal, 229 principal, certs, subject); 230 if(enableAudit) 231 successAudit(oldPrincipal,principal); 232 } 233 else 234 { 235 if (trace) 236 { 237 log.trace("User: " + principal + " is NOT authenticated"); 238 } 239 if(enableAudit) 240 failureAudit(principal); 241 principal = null; 242 } 243 } 244 catch (NamingException e) 245 { 246 log.error("Error during authenticate", e); 247 if(enableAudit) 248 errorAudit(principal,e); 249 } 250 return principal; 251 } 252 253 269 public Principal authenticate(String username, String digest, String nonce, 270 String nc, String cnonce, String qop, String realm, String md5a2) 271 { 272 Principal principal = null; 273 Context securityCtx = getSecurityContext(); 274 if (securityCtx == null) 275 { 276 if (trace) 277 { 278 log.trace("No security context for authenticate(String, String)"); 279 } 280 return null; 281 } 282 283 Principal caller = (Principal ) SecurityAssociationValve.userPrincipal.get(); 284 if (caller == null && username == null && digest == null) 285 { 286 return null; 287 } 288 289 try 290 { 291 DigestCallbackHandler handler = new DigestCallbackHandler(username, nonce, 292 nc, cnonce, qop, realm, md5a2); 293 CallbackHandlerPolicyContextHandler.setCallbackHandler(handler); 294 295 SubjectSecurityManager securityMgr = (SubjectSecurityManager) securityCtx.lookup("securityMgr"); 297 principal = new SimplePrincipal(username); 298 Subject subject = new Subject (); 299 if (securityMgr.isValid(principal, digest, subject)) 300 { 301 log.trace("User: " + username + " is authenticated"); 302 SecurityAssociationActions.setPrincipalInfo(principal, digest, subject); 303 securityDomain = securityMgr.getSecurityDomain(); 304 this.establishSecurityContext(securityDomain, 306 principal, digest, subject); 307 308 RealmMapping realmMapping = (RealmMapping) securityCtx.lookup("realmMapping"); 310 Principal oldPrincipal = principal; 311 principal = realmMapping.getPrincipal(oldPrincipal); 312 if (trace) 313 { 314 log.trace("Mapped from input principal: " + oldPrincipal 315 + "to: " + principal); 316 } 317 principal = getCachingPrincpal(realmMapping, oldPrincipal, 319 principal, digest, subject); 320 if(enableAudit) 321 successAudit(oldPrincipal,principal); 322 } 323 else 324 { 325 if(enableAudit) 326 failureAudit(principal); 327 principal = null; 328 if (trace) 329 { 330 log.trace("User: " + username + " is NOT authenticated"); 331 } 332 } 333 } 334 catch (NamingException e) 335 { 336 principal = null; 337 log.error("Error during authenticate", e); 338 if(enableAudit) 339 errorAudit(principal,e); 340 } 341 finally 342 { 343 CallbackHandlerPolicyContextHandler.setCallbackHandler(null); 344 } 345 if (trace) 346 { 347 log.trace("End authenticate, principal=" + principal); 348 } 349 return principal; 350 } 351 352 360 public Principal authenticate(String username, String credentials) 361 { 362 if (trace) 363 { 364 log.trace("Begin authenticate, username=" + username); 365 } 366 Principal principal = null; 367 Context securityCtx = getSecurityContext(); 368 if (securityCtx == null) 369 { 370 if (trace) 371 { 372 log.trace("No security context for authenticate(String, String)"); 373 } 374 return null; 375 } 376 377 Principal caller = (Principal ) SecurityAssociationValve.userPrincipal.get(); 378 if (caller == null && username == null && credentials == null) 379 { 380 return null; 381 } 382 383 try 384 { 385 SubjectSecurityManager securityMgr = (SubjectSecurityManager) securityCtx.lookup("securityMgr"); 387 principal = new SimplePrincipal(username); 388 Subject subject = new Subject (); 389 if (securityMgr.isValid(principal, credentials, subject)) 390 { 391 log.trace("User: " + username + " is authenticated"); 392 SecurityAssociationActions.setPrincipalInfo(principal, credentials, subject); 393 securityDomain = securityMgr.getSecurityDomain(); 394 this.establishSecurityContext(securityDomain, 396 principal, credentials, subject); 397 RealmMapping realmMapping = (RealmMapping) securityCtx.lookup("realmMapping"); 399 Principal oldPrincipal = principal; 400 principal = realmMapping.getPrincipal(oldPrincipal); 401 if (trace) 402 { 403 log.trace("Mapped from input principal: " + oldPrincipal 404 + "to: " + principal); 405 } 406 principal = getCachingPrincpal(realmMapping, oldPrincipal, 408 principal, credentials, subject); 409 if(enableAudit) 410 successAudit(oldPrincipal,principal); 411 } 412 else 413 { 414 if(enableAudit) 415 failureAudit(principal); 416 if (trace) 417 { 418 log.trace("User: " + username + " is NOT authenticated"); 419 } 420 principal = null; 421 } 422 } 423 catch (NamingException e) 424 { 425 principal = null; 426 log.error("Error during authenticate", e); 427 if(enableAudit) 428 errorAudit(principal,e); 429 } 430 if (trace) 431 { 432 log.trace("End authenticate, principal=" + principal); 433 } 434 return principal; 435 } 436 437 445 public Principal authenticate(String username, byte[] credentials) 446 { 447 return authenticate(username, new String (credentials)); 448 } 449 450 456 public SecurityConstraint[] findSecurityConstraints(Request request, 457 org.apache.catalina.Context context) 458 { 459 SecurityConstraint[] scarr = super.findSecurityConstraints(request, context); 460 if( (scarr == null || scarr.length == 0) 461 && this.unprotectedResourceDelegation) 462 { 463 scarr = getSecurityConstraintsFromProvider(request, context); 464 } 465 return scarr; 466 } 467 468 472 public boolean hasResourcePermission(Request request, Response response, 473 SecurityConstraint[] securityConstraints, org.apache.catalina.Context context) 474 throws IOException 475 { 476 Wrapper servlet = request.getWrapper(); 477 if (servlet != null) 478 { 479 activeRequest.set(getServletName(servlet)); 480 } 481 482 boolean baseDecision = ignoreBaseDecision ? true : 483 super.hasResourcePermission(request,response, 484 securityConstraints, context); 485 486 Subject caller = this.establishSubjectContext(request.getPrincipal()); 487 488 Map map = new HashMap (); 489 map.put(ResourceKeys.WEB_REQUEST, request); 490 map.put(ResourceKeys.WEB_RESPONSE, response); 491 map.put(ResourceKeys.WEB_SECURITY_CONSTRAINTS, securityConstraints); 492 map.put(ResourceKeys.WEB_CONTEXT, context); 493 map.put(ResourceKeys.CALLER_SUBJECT, caller); 494 map.put(ResourceKeys.RESOURCE_PERM_CHECK, Boolean.TRUE); 495 int permit = authorize(map); 496 boolean authzDecision = (permit == AuthorizationContext.PERMIT); 497 boolean finalDecision = baseDecision && authzDecision; 498 if(trace) 499 log.trace("hasResourcePerm:RealmBase says:" + baseDecision + 500 "::Authz framework says:" + authzDecision + ":final=" + finalDecision); 501 if( finalDecision == false ) 502 { 503 response.sendError(HttpServletResponse.SC_FORBIDDEN, 504 sm.getString("realmBase.forbidden")); 505 } 506 return finalDecision; 507 } 508 509 521 public boolean hasRole(Principal principal, String role) 522 { 523 String servletName = (String ) activeRequest.get(); 524 if(servletName == null) 525 throw new IllegalStateException ("servletName is null"); 526 WebMetaData metaData = (WebMetaData) SecurityAssociationValve.activeWebMetaData.get(); 527 String roleName = role; 528 529 532 if(metaData != null) 533 { 534 List roleRefs = metaData.getSecurityRoleRefs(servletName); 535 int len = roleRefs != null ? roleRefs.size() : 0; 536 for(int n = 0; n < len; n ++) 537 { 538 SecurityRoleRefMetaData ref = (SecurityRoleRefMetaData) roleRefs.get(n); 539 if( ref.getLink().equals(role) ) 540 { 541 roleName = ref.getName(); 542 break; 543 } 544 } 545 } 546 547 boolean baseDecision = ignoreBaseDecision ? true : super.hasRole(principal, role); 548 Map map = new HashMap (); 549 map.put(ResourceKeys.ROLENAME, roleName); 550 map.put(ResourceKeys.HASROLE_PRINCIPAL, principal); 551 map.put(ResourceKeys.ROLEREF_PERM_CHECK, Boolean.TRUE); 552 map.put(ResourceKeys.SERVLET_NAME, servletName); 553 map.put(ResourceKeys.PRINCIPAL_ROLES, this.getPrincipalRoles(principal)); 554 int permit = authorize(map); 555 boolean authzDecision = (permit == AuthorizationContext.PERMIT); 556 boolean finalDecision = baseDecision && authzDecision; 557 if(trace) 558 log.trace("hasRole:RealmBase says:" + baseDecision + 559 "::Authz framework says:" + authzDecision + ":final=" + finalDecision); 560 561 return finalDecision; 562 } 563 564 567 public boolean hasUserDataPermission(Request request, Response response, 568 SecurityConstraint[] constraints) throws IOException 569 { 570 Principal requestPrincipal = request.getPrincipal(); 571 establishSubjectContext(requestPrincipal); 572 Map map = new HashMap (); 573 map.put(ResourceKeys.WEB_REQUEST, request); 574 map.put(ResourceKeys.WEB_RESPONSE, response); 575 map.put(ResourceKeys.WEB_SECURITY_CONSTRAINTS, constraints); 576 map.put(ResourceKeys.USERDATA_PERM_CHECK, Boolean.TRUE); 577 int permit = authorize(map); 578 boolean ok = (permit == AuthorizationContext.PERMIT); 579 580 583 if( ok == false ) 584 ok = super.hasUserDataPermission(request, response, constraints); 585 return ok; 586 } 587 588 603 protected Principal getCachingPrincpal(RealmMapping realmMapping, 604 Principal authPrincipal, Principal callerPrincipal, Object credential, 605 Subject subject) 606 { 607 Set userRoles = realmMapping.getUserRoles(authPrincipal); 609 ArrayList roles = new ArrayList (); 610 if (userRoles != null) 611 { 612 Iterator iterator = userRoles.iterator(); 613 while (iterator.hasNext()) 614 { 615 Principal role = (Principal ) iterator.next(); 616 roles.add(role.getName()); 617 } 618 } 619 JBossGenericPrincipal gp = new JBossGenericPrincipal(this, subject, 620 authPrincipal, callerPrincipal, credential, roles, userRoles); 621 return gp; 622 } 623 624 628 protected String getName() 629 { 630 return getClass().getName(); 631 } 632 633 636 protected String getPassword(String username) 637 { 638 String password = null; 639 return password; 640 } 641 642 645 protected Principal getPrincipal(String username) 646 { 647 return new SimplePrincipal(username); 648 } 649 650 651 652 657 static String requestURI(Request request) 658 { 659 String uri = request.getMappingData().requestPath.getString(); 660 if( uri == null || uri.equals("/") ) 661 { 662 uri = ""; 663 } 664 return uri; 665 } 666 667 668 675 protected Set getPrincipalRoles(Principal principal) 676 { 677 if( (principal instanceof GenericPrincipal) == false ) 678 throw new IllegalStateException ("Expected GenericPrincipal, but saw: "+principal.getClass()); 679 GenericPrincipal gp = (GenericPrincipal) principal; 680 String [] roleNames = gp.getRoles(); 681 Set userRoles = new HashSet (); 682 if( roleNames != null ) 683 { 684 for(int n = 0; n < roleNames.length; n ++) 685 { 686 SimplePrincipal sp = new SimplePrincipal(roleNames[n]); 687 userRoles.add(sp); 688 } 689 } 690 return userRoles; 691 } 692 693 private int authorize(Map map) 697 { 698 AuthorizationManager authzMgr = this.getAuthorizationManager(); 699 if(authzMgr == null) 700 throw new IllegalStateException ("Authorization manager is null"); 701 702 map.put(ResourceKeys.AUTHORIZATION_MANAGER, authzMgr); 703 Map readOnlyMap = Collections.unmodifiableMap(map); 704 WebResource webResource = new WebResource(readOnlyMap); 705 int permit = AuthorizationContext.DENY; 706 try 707 { 708 permit = authzMgr.authorize(webResource); 709 String level = (permit == AuthorizationContext.PERMIT ? AuditLevel.SUCCESS : AuditLevel.FAILURE); 710 authorizationAudit(level,webResource); 711 } 712 catch (AuthorizationException e) 713 { 714 if(trace) 715 log.trace("hasResourcePermission:",e); 716 permit = AuthorizationContext.DENY; 717 authorizationAudit(AuditLevel.ERROR,webResource); 718 } 719 return permit; 720 } 721 722 731 private Subject establishSubjectContext(Principal principal) 732 { 733 Subject caller = null; 734 try 735 { 736 caller = (Subject ) PolicyContext.getContext(SUBJECT_CONTEXT_KEY); 737 } 738 catch (PolicyContextException e) 739 { 740 if( trace ) 741 log.trace("Failed to get subject from PolicyContext", e); 742 } 743 744 if( caller == null ) 745 { 746 if( principal instanceof JBossGenericPrincipal ) 748 { 749 JBossGenericPrincipal jgp = (JBossGenericPrincipal) principal; 750 caller = jgp.getSubject(); 751 if (trace) 753 log.trace("Restoring principal info from cache"); 754 SecurityAssociationActions.setPrincipalInfo(jgp.getAuthPrincipal(), 755 jgp.getCredentials(), jgp.getSubject()); 756 } 757 } 758 return caller; 759 } 760 761 765 private AuthorizationManager getAuthorizationManager() 766 { 767 AuthorizationManager am = null; 768 try 769 { 770 am = (AuthorizationManager)getSecurityContext().lookup("authorizationMgr"); 771 } 772 catch (Exception e) 773 { 774 if(trace) 775 log.trace("Lookup of authorization manager failed", e); 776 } 777 return am; 778 } 779 780 private Context getSecurityContext() 781 { 782 Context securityCtx = null; 783 try 785 { 786 InitialContext iniCtx = new InitialContext (); 787 securityCtx = (Context ) iniCtx.lookup("java:comp/env/security"); 788 } 789 catch (NamingException e) 790 { 791 } 793 return securityCtx; 794 } 795 796 804 private SecurityConstraint[] getSecurityConstraintsFromProvider(Request request, 805 org.apache.catalina.Context context) 806 { 807 SecurityConstraint[] scarr = null; 808 Class [] sig = {Request .class, Context .class}; 809 Object [] args = {request, context}; 810 811 Method findsc = null; 812 813 try 815 { 816 Policy policy = Policy.getPolicy(); 817 findsc = policy.getClass().getMethod("findSecurityConstraints", sig); 818 scarr = (SecurityConstraint[])findsc.invoke(policy, args); 819 }catch(Throwable t) 820 { 821 if(trace) 822 log.error("Error obtaining security constraints from policy",t); 823 } 824 if(scarr == null || scarr.length == 0) 827 { 828 if(securityConstraintProviderClass == "" || 829 securityConstraintProviderClass.length() == 0) 830 { 831 if(trace) 832 log.trace("unprotectedResourceDelegation is true "+ 833 "but securityConstraintProviderClass is empty"); 834 } 835 else 836 try 838 { 839 Class clazz = Thread.currentThread().getContextClassLoader().loadClass(securityConstraintProviderClass); 840 Object obj = clazz.newInstance(); 841 findsc = clazz.getMethod("findSecurityConstraints", sig); 842 if(trace) 843 log.trace("findSecurityConstraints method found in securityConstraintProviderClass"); 844 scarr = (SecurityConstraint[])findsc.invoke(obj, args); 845 } 846 catch (Throwable t) 847 { 848 log.error("Error instantiating "+securityConstraintProviderClass,t); 849 } 850 } 851 return scarr; 852 } 853 854 872 private String getServletName(Wrapper servlet) 873 { 874 String [] mappings = servlet.findMappings(); 876 if(trace) 877 log.trace("[getServletName:servletmappings="+mappings + 878 ":servlet.getName()="+servlet.getName()+"]"); 879 if("jsp".equals(servlet.getName()) 880 && (mappings != null && mappings[0].indexOf("*.jsp")> -1)) 881 return ""; 882 else 883 return servlet.getName(); 884 } 885 886 private void audit(String level, 887 Map contextMap, Exception e) 888 { 889 String requestInfo = ""; 890 try 891 { 892 HttpServletRequest hsr = (HttpServletRequest )PolicyContext.getContext(SecurityConstants.WEB_REQUEST_KEY); 893 requestInfo = WebUtil.deriveUsefulInfo(hsr); 894 contextMap.put("request", requestInfo); 895 } 896 catch (PolicyContextException pe) 897 { 898 if(trace) 899 log.trace("Error obtaining the servlet request:", pe); 900 } 901 contextMap.put("Source", getClass().getName()); 902 SecurityContext sc = SecurityAssociationActions.getSecurityContext(securityDomain); 903 AuditContext ac = sc != null ? sc.getAuditContext(): 904 AuditManager.getAuditContext(securityDomain); 905 AuditEvent ae = new AuditEvent(level); 906 ae.setContextMap(contextMap); 907 ae.setUnderlyingException(e); 908 ac.audit(ae); 909 } 910 911 private void successAudit(Principal callerPrincipal, Principal principal) 912 { 913 Map cmap = new HashMap (); 914 cmap.put("principal", principal); 915 cmap.put("CallerPrincipal", callerPrincipal); 916 audit(AuditLevel.SUCCESS,cmap,null); 917 } 918 919 private void failureAudit(Principal principal) 920 { 921 Map cmap = new HashMap (); 922 cmap.put("principal", principal); 923 audit(AuditLevel.FAILURE,cmap,null); 924 } 925 926 private void errorAudit(Principal principal, Exception e) 927 { 928 Map cmap = new HashMap (); 929 cmap.put("principal", principal); 930 audit(AuditLevel.ERROR,cmap,e); 931 } 932 933 private void authorizationAudit(String level, WebResource resource) 934 { 935 if(!enableAudit) 936 return; 937 Map cmap = new HashMap (); 938 cmap.putAll(resource.getMap()); 939 audit(level,cmap,null); 940 } 941 942 private void establishSecurityContext(String domain, Principal p, Object cred, 944 Subject subject) 945 { 946 JBossSecurityContext jsc = new JBossSecurityContext(domain); 947 SubjectInfo si = jsc.new SubjectInfo(); 948 si.setAuthenticatedSubject(subject); 949 si.setAuthenticationCredential(cred); 950 si.setAuthenticationPrincipal(p); 951 jsc.setSubjectInfo(si); 952 SecurityAssociationActions.setSecurityContext(jsc, domain); 953 if(trace) 954 log.trace("Established Security Context for " + domain); 955 } 956 } 957 | Popular Tags |