blob: 92f7843023b4238b8396f943a492591c3984787a [file] [log] [blame]
/*
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.enterprise.security.ee;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.deployment.common.SecurityRoleMapper;
import org.glassfish.security.common.Role;
import com.sun.appserv.security.AuditModule;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.EjbDescriptor;
import com.sun.enterprise.deployment.EjbIORConfigurationDescriptor;
import com.sun.enterprise.deployment.MethodDescriptor;
import com.sun.enterprise.deployment.MethodPermission;
import com.sun.enterprise.deployment.RunAsIdentityDescriptor;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.deployment.WebComponentDescriptor;
import com.sun.enterprise.deployment.web.AuthorizationConstraint;
import com.sun.enterprise.deployment.web.LoginConfiguration;
import com.sun.enterprise.deployment.web.SecurityConstraint;
import com.sun.enterprise.deployment.web.SecurityRole;
import com.sun.enterprise.deployment.web.UserDataConstraint;
import com.sun.enterprise.deployment.web.WebResourceCollection;
//V3:Commented import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.logging.LogDomains;
import jakarta.servlet.http.HttpServletRequest;
/**
* Audit support class.
*
* <P>
* This class provides convenience methods for producing audit output. Audit output is logged using the standard iAS
* logger SECURITYLOGGER. However, audit output is only produced if auditing is active. Auditing is configured in
* server.xml in the security-service element.
*
* <P>
* Audit output if logged with Level.WARNING.
*
* <P>
* Some diagnostic methods are also provided for debugging.
*
*/
public class Audit extends AuditModule {
private static final String AUDIT_ON = "auditOn";
private static boolean auditFlag = false;
private static Logger logger = LogDomains.getLogger(Audit.class, LogDomains.SECURITY_LOGGER);
/*
* private static String strPrivateAudit = null; private static String strDenied = null; private static String strOK =
* null; private static String strMethodName = null; private static String strSession = null;
*/
/**
* Check auditing state.
*
* @returns True if auditing is active currently.
*
*/
public static boolean isActive() {
return auditFlag;
}
@Override
public void init(Properties props) {
super.init(props);
String audit = props.getProperty(AUDIT_ON);
auditFlag = audit == null ? false : Boolean.valueOf(audit);
}
/**
* Invoked post authentication request for a user in a given realm
*
* @param user username for whom the authentication request was made
* @param realm the realm name under which the user is authenticated.
* @param success the status of the authentication
*/
@Override
public void authentication(String user, String realm, boolean success) {
if (auditFlag) {
StringBuffer sbuf = new StringBuffer("Audit: Authentication for user = (");
sbuf.append(user);
sbuf.append(") under realm = (");
sbuf.append(realm).append(") returned = ").append(success);
logger.log(Level.INFO, sbuf.toString());
}
}
/**
* Invoked post web authorization request.
*
* @param user the username for whom the authorization was performed
* @param req the HttpRequest object for the web request
* @param type either hasResourcePermission, hasUserDataPermission or hasRoleRefPermission
* @param success the status of the web authorization request
*/
@Override
public void webInvocation(String user, HttpServletRequest req, String type, boolean success) {
if (auditFlag) {
StringBuilder sbuf = new StringBuilder("Audit: [Web] Authorization for user = (");
sbuf.append(user).append(") and permission type = (").append(type).append(") for request ");
sbuf.append(req.getMethod()).append(" ").append(req.getRequestURI()).append(" returned =").append(success);
logger.log(Level.INFO, sbuf.toString());
}
}
/**
* Invoked post ejb authorization request.
*
* @param user the username for whom the authorization was performed
* @param ejb the ejb name for which this authorization was performed
* @param method the method name for which this authorization was performed
* @param success the status of the ejb authorization request
*/
@Override
public void ejbInvocation(String user, String ejb, String method, boolean success) {
if (auditFlag) {
// Modified from StringBuffer to StringBuilder
StringBuilder sbuf = new StringBuilder("Audit: [EJB] Authorization for user =");
sbuf.append(user).append(" for ejb = (");
sbuf.append(ejb).append(") method = (").append(method).append(") returned =").append(success);
logger.log(Level.INFO, sbuf.toString());
}
}
/**
* Invoked post ejb authorization request.
*
* @param user the username for whom the authorization was performed
* @param ejb the ejb name for which this authorization was performed
* @param method the method name for which this authorization was performed
* @param success the status of the ejb authorization request
*/
/**
* Invoked during validation of the web service request
*
* @param uri The URL representation of the web service endpoint
* @param endpoint The name of the endpoint representation
* @param success the status of the web service request validation
*/
@Override
public void webServiceInvocation(String uri, String endpoint, boolean success) {
if (auditFlag) {
StringBuilder sbuf = new StringBuilder("Audit: [WebService] ");
sbuf.append("uri: ").append(uri);
sbuf.append("endpoint: ").append(endpoint);
sbuf.append(", valid request =").append(success);
logger.log(Level.INFO, sbuf.toString());
}
}
/**
* Invoked during validation of the web service request
*
* @param endpoint The URL representation of the web service endpoint
* @param success the status of the web service request validation
*/
@Override
public void ejbAsWebServiceInvocation(String endpoint, boolean success) {
if (auditFlag) {
StringBuilder sbuf = new StringBuilder("Audit: [EjbAsWebService] ");
sbuf.append("endpoint : ").append(endpoint).append(", valid request =").append(success);
logger.log(Level.INFO, sbuf.toString());
}
}
/**
* Invoked upon completion of the server startup
*/
@Override
public void serverStarted() {
if (auditFlag) {
logger.log(Level.INFO, "Audit: Application server startup complete");
}
}
/**
* Invoked upon completion of the server shutdown
*/
@Override
public void serverShutdown() {
if (auditFlag) {
logger.log(Level.INFO, "Audit: Application server shutdown complete");
}
}
/**
* Initialize auditing. This reads the server.xml configuration to determine whether audit is turned on or off.
*
*/
/*
* public static void init() { try { ConfigContext configContext =
* ApplicationServer.getServerContext().getConfigContext(); assert(configContext != null);
*
* Server configBean = ServerBeansFactory.getServerBean(configContext); assert(configBean != null);
*
* SecurityService securityBean = ServerBeansFactory.getSecurityServiceBean(configContext); assert(securityBean !=
* null);
*
* auditFlag = securityBean.isAuditEnabled();
*
* } catch (Exception e) { logger.log(Level.WARNING, "audit.badinit", e); }
*
* if (auditFlag) { logger.info("audit.enabled"); }
*
* // load i18n message bits for audit entries ResourceBundle resBundle = logger.getResourceBundle(); strPrivateAudit =
* resBundle.getString("audit.string_private_audit"); strDenied = " " + resBundle.getString("audit.denied"); strOK = " "
* + resBundle.getString("audit.ok"); strMethodName = " " + resBundle.getString("audit.methodname"); strSession = " " +
* resBundle.getString("audit.session"); }
*
*/
/**
* Log an EJB method invocation.
*
* @param user Effective user for the invocation.
* @param ejb EJB name.
* @param method Method name.
* @param success True if the invocation was allowed, false if denied.
*
*/
/*
* public static void ejbMethodInvocation(SecurityContext secCtx, EJBLocalRemoteObject ejbObj, Method method, boolean
* success) { if (!logger.isLoggable(Level.INFO)) { return; }
*
* String user = "(null)"; if (secCtx != null) { Principal p = secCtx.getCallerPrincipal(); if (p!=null) { user =
* p.getName(); } }
*
* String ejb = "(N/A)"; if (ejbObj != null) { ejb = ejbObj.toString(); }
*
* String meth = "(N/A)"; if (method != null) { meth = method.toString(); }
*
* StringBuffer sb = new StringBuffer(); sb.append(strPrivateAudit); // "Audit: principal="
*
* if(user != null) { sb.append(user); } else { sb.append("(null)"); }
*
* sb.append(" ejb="); sb.append(ejb); sb.append(strMethodName); // " method=" sb.append(method); if (success) {
* sb.append(strOK); // " OK" } else { sb.append(strDenied); // " DENIED" }
*
* logger.info(sb.toString()); }
*
*/
/**
* Log a servlet invocation.
*
* @param req The HttpRequest.
* @param success True if the invocation was allowed, false if denied.
*
*/
/*
* public static void webInvocation(HttpRequest req, boolean success) { /// DO NOTHING FOR NOW. //if
* (!logger.isLoggable(Level.INFO) || !auditFlag) { // return; //}
*
* //if (req == null) { // logger.fine("Audit: No HttpRequest available."); // return; //}
*
* //if (!(req instanceof HttpRequestBase)) { // logger.fine("Audit internal error, class: " + req.getClass()); //
* return; //}
*
* //HttpRequestBase reqs = (HttpRequestBase)req;
*
* //StringBuffer sb = new StringBuffer(); //sb.append(strPrivateAudit); // "Audit: principal="
*
* //String user = reqs.getRemoteUser(); //if (user != null) { // sb.append(user); //} else { // sb.append("(null)");
* //}
*
* //sb.append(" "); //sb.append(reqs.getMethod()); //sb.append(" "); //sb.append(reqs.getRequestURI());
* //sb.append(strSession); // " session=" //sb.append(reqs.getRequestedSessionId()); //if (success) { //
* sb.append(strOK); // " OK" //} else { // sb.append(strDenied); // " DENIED" //}
*
* //logger.info(sb.toString()); }
*
*/
/**
* Diagnostic method. Read roles and ACLs from the given Application and dump a somewhat organized summary of what has
* been set. This can be used to diagnose deployment or runtime deployment errors as well as to help in configuring
* application descriptors.
*
* <P>
* Implementation is not particularly efficient but this is only called for debugging purposes at startup. All errors
* are ignored.
*
* @param app Application object to analyze.
*
*/
public static void showACL(Application app) {
if (!isActive() || !logger.isLoggable(Level.FINEST)) {
return;
}
try {
dumpDiagnostics(app);
} catch (Throwable e) {
logger.fine("Error while showing ACL diagnostics: " + e.toString());
}
}
/**
* Do the work for showACL().
*
*/
private static void dumpDiagnostics(Application app) {
logger.finest("====[ Role and ACL Summary ]==========");
if (!app.isVirtual()) {
logger.finest("Summary for application: " + app.getRegistrationName());
} else {
logger.finest("Standalone module.");
}
logger.finest("EJB components: " + getEjbComponentCount(app));
logger.finest("Web components: " + getWebComponentCount(app));
Iterator i;
StringBuffer sb;
// show all roles with associated group & user mappings
Set allRoles = app.getRoles();
if (allRoles == null) {
logger.finest("- No roles present.");
return;
}
SecurityRoleMapper rmap = app.getRoleMapper();
if (rmap == null) {
logger.finest("- No role mappings present.");
return;
}
i = allRoles.iterator();
logger.finest("--[ Configured roles and mappings ]--");
HashMap allRoleMap = new HashMap();
while (i.hasNext()) {
Role r = (Role) i.next();
logger.finest(" [" + r.getName() + "]");
allRoleMap.put(r.getName(), new HashSet());
sb = new StringBuffer();
sb.append(" is mapped to groups: ");
Enumeration grps = rmap.getGroupsAssignedTo(r);
while (grps.hasMoreElements()) {
sb.append(grps.nextElement());
sb.append(" ");
}
logger.finest(sb.toString());
sb = new StringBuffer();
sb.append(" is mapped to principals: ");
Enumeration users = rmap.getUsersAssignedTo(r);
while (users.hasMoreElements()) {
sb.append(users.nextElement());
sb.append(" ");
}
logger.finest(sb.toString());
}
// Process all EJB modules
Set ejbDescriptorSet = app.getBundleDescriptors(EjbBundleDescriptor.class);
i = ejbDescriptorSet.iterator();
while (i.hasNext()) {
EjbBundleDescriptor bundle = (EjbBundleDescriptor) i.next();
logger.finest("--[ EJB module: " + bundle.getName() + " ]--");
Set ejbs = bundle.getEjbs();
Iterator it = ejbs.iterator();
while (it.hasNext()) {
EjbDescriptor ejb = (EjbDescriptor) it.next();
logger.finest("EJB: " + ejb.getEjbClassName());
// check and show run-as if present
if (!ejb.getUsesCallerIdentity()) {
RunAsIdentityDescriptor runas = ejb.getRunAsIdentity();
if (runas == null) {
logger.finest(" (ejb does not use caller " + "identity)");
} else {
String role = runas.getRoleName();
String user = runas.getPrincipal();
logger.finest(" Will run-as: Role: " + role + " Principal: " + user);
if (role == null || "".equals(role) || user == null || "".equals(user)) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("*** Configuration error!");
}
}
}
}
// iterate through available methods
logger.finest(" Method to Role restriction list:");
Set methods = ejb.getMethodDescriptors();
Iterator si = methods.iterator();
while (si.hasNext()) {
MethodDescriptor md = (MethodDescriptor) si.next();
logger.finest(" " + md.getFormattedString());
Set perms = ejb.getMethodPermissionsFor(md);
StringBuffer rbuf = new StringBuffer();
rbuf.append(" can only be invoked by: ");
Iterator sip = perms.iterator();
boolean unchecked = false, excluded = false, roleBased = false;
while (sip.hasNext()) {
MethodPermission p = (MethodPermission) sip.next();
if (p.isExcluded()) {
excluded = true;
logger.finest(" excluded - can not " + "be invoked");
} else if (p.isUnchecked()) {
unchecked = true;
logger.finest(" unchecked - can be " + "invoked by all");
} else if (p.isRoleBased()) {
roleBased = true;
Role r = p.getRole();
rbuf.append(r.getName());
rbuf.append(" ");
// add to role's accessible list
HashSet ram = (HashSet) allRoleMap.get(r.getName());
ram.add(bundle.getName() + ":" + ejb.getEjbClassName() + "." + md.getFormattedString());
}
}
if (roleBased) {
logger.finest(rbuf.toString());
if (excluded || unchecked) {
logger.finest("*** Configuration error!");
}
} else if (unchecked) {
if (excluded) {
logger.finest("*** Configuration error!");
}
Set rks = allRoleMap.keySet();
Iterator rksi = rks.iterator();
while (rksi.hasNext()) {
HashSet ram = (HashSet) allRoleMap.get(rksi.next());
ram.add(bundle.getName() + ":" + ejb.getEjbClassName() + "." + md.getFormattedString());
}
} else if (!excluded) {
logger.finest("*** Configuration error!");
}
}
// IOR config for this ejb
logger.finest(" IOR configuration:");
Set iors = ejb.getIORConfigurationDescriptors();
if (iors != null) {
Iterator iorsi = iors.iterator();
while (iorsi.hasNext()) {
EjbIORConfigurationDescriptor ior = (EjbIORConfigurationDescriptor) iorsi.next();
StringBuffer iorsb = new StringBuffer();
iorsb.append("realm=");
iorsb.append(ior.getRealmName());
iorsb.append(", integrity=");
iorsb.append(ior.getIntegrity());
iorsb.append(", trust-in-target=");
iorsb.append(ior.getEstablishTrustInTarget());
iorsb.append(", trust-in-client=");
iorsb.append(ior.getEstablishTrustInClient());
iorsb.append(", propagation=");
iorsb.append(ior.getCallerPropagation());
iorsb.append(", auth-method=");
iorsb.append(ior.getAuthenticationMethod());
logger.finest(iorsb.toString());
}
}
}
}
// show role->accessible methods list
logger.finest("--[ EJB methods accessible by role ]--");
Set rks = allRoleMap.keySet();
Iterator rksi = rks.iterator();
while (rksi.hasNext()) {
String roleName = (String) rksi.next();
logger.finest(" [" + roleName + "]");
HashSet ram = (HashSet) allRoleMap.get(roleName);
Iterator rami = ram.iterator();
while (rami.hasNext()) {
String meth = (String) rami.next();
logger.finest(" " + meth);
}
}
// Process all Web modules
Set webDescriptorSet = app.getBundleDescriptors(WebBundleDescriptor.class);
i = webDescriptorSet.iterator();
while (i.hasNext()) {
WebBundleDescriptor wbd = (WebBundleDescriptor) i.next();
logger.finest("--[ Web module: " + wbd.getContextRoot() + " ]--");
// login config
LoginConfiguration lconf = wbd.getLoginConfiguration();
if (lconf != null) {
logger.finest(" Login config: realm=" + lconf.getRealmName() + ", method=" + lconf.getAuthenticationMethod() + ", form="
+ lconf.getFormLoginPage() + ", error=" + lconf.getFormErrorPage());
}
// get WebComponentDescriptorsSet() info
logger.finest(" Contains components:");
Set webComps = wbd.getWebComponentDescriptors();
Iterator webCompsIt = webComps.iterator();
while (webCompsIt.hasNext()) {
WebComponentDescriptor wcd = (WebComponentDescriptor) webCompsIt.next();
StringBuffer name = new StringBuffer();
name.append(" - " + wcd.getCanonicalName());
name.append(" [ ");
Enumeration urlPs = wcd.getUrlPatterns();
while (urlPs.hasMoreElements()) {
name.append(urlPs.nextElement().toString());
name.append(" ");
}
name.append("]");
logger.finest(name.toString());
RunAsIdentityDescriptor runas = wcd.getRunAsIdentity();
if (runas != null) {
String role = runas.getRoleName();
String user = runas.getPrincipal();
logger.finest(" Will run-as: Role: " + role + " Principal: " + user);
if (role == null || "".equals(role) || user == null || "".equals(user)) {
logger.finest("*** Configuration error!");
}
}
}
// security constraints
logger.finest(" Security constraints:");
Enumeration scEnum = wbd.getSecurityConstraints();
while (scEnum.hasMoreElements()) {
SecurityConstraint sc = (SecurityConstraint) scEnum.nextElement();
for (WebResourceCollection wrc : sc.getWebResourceCollections()) {
// show list of methods for this collection
StringBuffer sbm = new StringBuffer();
for (String httpMethod : wrc.getHttpMethods()) {
sbm.append(httpMethod);
sbm.append(" ");
}
logger.finest(" Using method: " + sbm.toString());
// and then list of url patterns
for (String urlPattern : wrc.getUrlPatterns()) {
logger.finest(" " + urlPattern);
}
} // end res.collection iterator
// show roles which apply to above set of collections
AuthorizationConstraint authCons = sc.getAuthorizationConstraint();
Enumeration rolesEnum = authCons.getSecurityRoles();
StringBuffer rsb = new StringBuffer();
rsb.append(" Accessible by roles: ");
while (rolesEnum.hasMoreElements()) {
SecurityRole sr = (SecurityRole) rolesEnum.nextElement();
rsb.append(sr.getName());
rsb.append(" ");
}
logger.finest(rsb.toString());
// show transport guarantee
UserDataConstraint udc = sc.getUserDataConstraint();
if (udc != null) {
logger.finest(" Transport guarantee: " + udc.getTransportGuarantee());
}
} // end sec.constraint
} // end webDescriptorSet.iterator
logger.finest("======================================");
}
/**
* The number of Web Components in this application. Current implementation only return the number of servlets inside
* the application, and not the JSPs since we cannot get that information from deployment descriptors.
*
* @return the number of Web Components
*/
private static int getWebComponentCount(Application app) {
int count = 0;
for (WebBundleDescriptor wbd : app.getBundleDescriptors(WebBundleDescriptor.class)) {
count = count + wbd.getWebComponentDescriptors().size();
}
return count;
}
/**
* The number of EJB JARs in this application.
*
* @return the number of EJB JARS
*/
private static int getEjbComponentCount(Application app) {
int count = 0;
for (EjbBundleDescriptor ejbd : app.getBundleDescriptors(EjbBundleDescriptor.class)) {
count = count + ejbd.getEjbs().size();
}
return count;
}
}