ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/realm/RealmBase.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/realm/RealmBase.java
Status
scanned
Type
file
Name
RealmBase.java
Extension
.java
Programming language
Java
Mime type
text/x-c
File type
C source, ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
57393 (56.0 KB)
MD5
00e11a3661b86ad350dd857553346e04
SHA1
5bd7c1647a00b9613499a1055fc9452d0d3837a7
SHA256
eb6fe7997c03aea895a0d3ff0c13e3aaac9e35c97f1cc2d1cc3354a53bc14e32
SHA512

      
    
SHA1_git
506445d2a1440a77ef294fd701381f4cac780c5f
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
RealmBase.java | 56.0 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.realm; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee; import jakarta.servlet.http.HttpServletResponse; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.CredentialHandler; import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; import org.apache.catalina.Realm; import org.apache.catalina.Server; import org.apache.catalina.Service; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.util.LifecycleMBeanBase; import org.apache.catalina.util.SessionConfig; import org.apache.catalina.util.ToStringUtil; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.IntrospectionUtils; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.HexUtils; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.security.ConcurrentMessageDigest; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSName; /** * Simple implementation of <b>Realm</b> that reads an XML file to configure the valid users, passwords, and roles. The * file format (and default file location) are identical to those currently supported by Tomcat 3.X. */ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { private static final Log log = LogFactory.getLog(RealmBase.class); /** * The character used for delimiting user attribute names. * <p> * Applies to some of the Realm implementations only. */ protected static final String USER_ATTRIBUTES_DELIMITER = ","; /** * The character used as wildcard in user attribute lists. Using it means <i>query all available user * attributes</i>. * <p> * Applies to some of the Realm implementations only. */ protected static final String USER_ATTRIBUTES_WILDCARD = "*"; private static final List<Class<? extends DigestCredentialHandlerBase>> credentialHandlerClasses = new ArrayList<>(); static { // Order is important since it determines the search order for a // matching handler if only an algorithm is specified when calling // main() credentialHandlerClasses.add(MessageDigestCredentialHandler.class); credentialHandlerClasses.add(SecretKeyCredentialHandler.class); } // ----------------------------------------------------- Instance Variables /** * The Container with which this Realm is associated. */ protected Container container = null; /** * Container log */ protected Log containerLog = null; private CredentialHandler credentialHandler; /** * The string manager for this package. */ protected static final StringManager sm = StringManager.getManager(RealmBase.class); /** * The property change support for this component. */ protected final PropertyChangeSupport support = new PropertyChangeSupport(this); /** * Should we validate client certificate chains when they are presented? */ protected boolean validate = true; /** * The name of the class to use for retrieving usernames from X509 certificates. */ protected String x509UsernameRetrieverClassName; /** * The object that will extract usernames from X509 client certificates. */ protected X509UsernameRetriever x509UsernameRetriever; /** * The all role mode. */ protected AllRolesMode allRolesMode = AllRolesMode.STRICT_MODE; /** * When processing users authenticated via the GSS-API, should any &quot;@...&quot; be stripped from the end of the * username? */ protected boolean stripRealmForGss = true; private int transportGuaranteeRedirectStatus = HttpServletResponse.SC_FOUND; /** * The comma separated names of user attributes to additionally query from the realm. These will be provided to the * user through the created Principal's <i>attributes</i> map. Support for this feature is optional. */ protected String userAttributes = null; /** * The list of user attributes to additionally query from the realm. These will be provided to the user through the * created Principal's <i>attributes</i> map. Support for this feature is optional. */ protected List<String> userAttributesList = null; // ------------------------------------------------------------- Properties /** * @return The HTTP status code used when the container needs to issue an HTTP redirect to meet the requirements of * a configured transport guarantee. */ public int getTransportGuaranteeRedirectStatus() { return transportGuaranteeRedirectStatus; } /** * Set the HTTP status code used when the container needs to issue an HTTP redirect to meet the requirements of a * configured transport guarantee. * * @param transportGuaranteeRedirectStatus The status to use. This value is not validated */ public void setTransportGuaranteeRedirectStatus(int transportGuaranteeRedirectStatus) { this.transportGuaranteeRedirectStatus = transportGuaranteeRedirectStatus; } @Override public CredentialHandler getCredentialHandler() { return credentialHandler; } @Override public void setCredentialHandler(CredentialHandler credentialHandler) { this.credentialHandler = credentialHandler; } @Override public Container getContainer() { return container; } @Override public void setContainer(Container container) { Container oldContainer = this.container; this.container = container; support.firePropertyChange("container", oldContainer, this.container); } /** * Return the all roles mode. * * @return A string representation of the current all roles mode */ public String getAllRolesMode() { return allRolesMode.toString(); } /** * Set the all roles mode. * * @param allRolesMode A string representation of the new all roles mode */ public void setAllRolesMode(String allRolesMode) { this.allRolesMode = AllRolesMode.toMode(allRolesMode); } /** * Return the "validate certificate chains" flag. * * @return The value of the validate certificate chains flag */ public boolean getValidate() { return validate; } /** * Set the "validate certificate chains" flag. * * @param validate The new validate certificate chains flag */ public void setValidate(boolean validate) { this.validate = validate; } /** * Gets the name of the class that will be used to extract usernames from X509 client certificates. * * @return The name of the class that will be used to extract usernames from X509 client certificates. */ public String getX509UsernameRetrieverClassName() { return x509UsernameRetrieverClassName; } /** * Sets the name of the class that will be used to extract usernames from X509 client certificates. The class must * implement X509UsernameRetriever. * * @param className The name of the class that will be used to extract usernames from X509 client certificates. * * @see X509UsernameRetriever */ public void setX509UsernameRetrieverClassName(String className) { this.x509UsernameRetrieverClassName = className; } public boolean isStripRealmForGss() { return stripRealmForGss; } public void setStripRealmForGss(boolean stripRealmForGss) { this.stripRealmForGss = stripRealmForGss; } /** * @return the comma separated names of user attributes to additionally query from realm */ public String getUserAttributes() { return userAttributes; } /** * Set the comma separated names of user attributes to additionally query from the realm. These will be provided to * the user through the created Principal's <i>attributes</i> map. In this map, each field value is bound to the * field's name, that is, the name of the field serves as the key of the mapping. * <p> * If set to the wildcard character, or, if the wildcard character is part of the comma separated list, all * available attributes - except the <i>password</i> attribute (as specified by <code>userCredCol</code>) - are * queried. The wildcard character is defined by constant {@link RealmBase#USER_ATTRIBUTES_WILDCARD}. It defaults to * the asterisk (*) character. * * @param userAttributes the comma separated names of user attributes */ public void setUserAttributes(String userAttributes) { this.userAttributes = userAttributes; } // --------------------------------------------------------- Public Methods @Override public void addPropertyChangeListener(PropertyChangeListener listener) { support.addPropertyChangeListener(listener); } @Override public Principal authenticate(String username) { if (username == null) { return null; } if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateSuccess", username)); } return getPrincipal(username); } @Override public Principal authenticate(String username, String credentials) { // No user or no credentials // Can't possibly authenticate, don't bother doing anything. if (username == null || credentials == null) { if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateFailure", username)); } return null; } // Look up the user's credentials String serverCredentials = getPassword(username); if (serverCredentials == null) { // User was not found // Waste a bit of time as not to reveal that the user does not exist. getCredentialHandler().mutate(credentials); if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateFailure", username)); } return null; } boolean validated = getCredentialHandler().matches(credentials, serverCredentials); if (validated) { if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateSuccess", username)); } return getPrincipal(username); } else { if (containerLog.isTraceEnabled()) { containerLog.trace(sm.getString("realmBase.authenticateFailure", username)); } return null; } } @Override public Principal authenticate(String username, String clientDigest, String nonce, String nc, String cnonce, String qop, String realm, String digestA2, String algorithm) { // In digest auth, digests are always lower case String digestA1 = getDigest(username, realm, algorithm); if (digestA1 == null) { return null; } digestA1 = digestA1.toLowerCase(Locale.ENGLISH); String serverDigestValue; if (qop == null) { serverDigestValue = digestA1 + ":" + nonce + ":" + digestA2; } else { serverDigestValue = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + digestA2; } byte[] valueBytes; try { valueBytes = serverDigestValue.getBytes(getDigestCharset()); } catch (UnsupportedEncodingException uee) { throw new IllegalArgumentException(sm.getString("realmBase.invalidDigestEncoding", getDigestEncoding()), uee); } String serverDigest = HexUtils.toHexString(ConcurrentMessageDigest.digest(algorithm, valueBytes)); if (log.isTraceEnabled()) { log.trace("Digest : " + clientDigest + " Username:" + username + " ClientDigest:" + clientDigest + " nonce:" + nonce + " nc:" + nc + " cnonce:" + cnonce + " qop:" + qop + " realm:" + realm + "digestA2:" + digestA2 + " Server digest:" + serverDigest); } if (serverDigest.equals(clientDigest)) { return getPrincipal(username); } return null; } @Override public Principal authenticate(X509Certificate[] certs) { if ((certs == null) || (certs.length < 1)) { return null; } // Check the validity of each certificate in the chain if (log.isTraceEnabled()) { log.trace("Authenticating client certificate chain"); } if (validate) { for (X509Certificate cert : certs) { if (log.isTraceEnabled()) { log.trace(" Checking validity for '" + cert.getSubjectX500Principal().toString() + "'"); } try { cert.checkValidity(); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("realmBase.validity"), e); } return null; } } } // Check the existence of the client Principal in our database return getPrincipal(certs[0]); } @Override public Principal authenticate(GSSContext gssContext, boolean storeCred) { if (gssContext.isEstablished()) { GSSName gssName = null; try { gssName = gssContext.getSrcName(); } catch (GSSException e) { log.warn(sm.getString("realmBase.gssNameFail"), e); } if (gssName != null) { GSSCredential gssCredential = null; if (storeCred) { if (gssContext.getCredDelegState()) { try { gssCredential = gssContext.getDelegCred(); } catch (GSSException e) { log.warn(sm.getString("realmBase.delegatedCredentialFail", gssName), e); } } else { if (log.isTraceEnabled()) { log.trace(sm.getString("realmBase.credentialNotDelegated", gssName)); } } } return getPrincipal(gssName, gssCredential, gssContext); } } else { log.error(sm.getString("realmBase.gssContextNotEstablished")); } // Fail in all other cases return null; } @Override public Principal authenticate(GSSName gssName, GSSCredential gssCredential) { if (gssName == null) { return null; } return getPrincipal(gssName, gssCredential); } /** * {@inheritDoc} * <p> * The default implementation is NO-OP. */ @Override public void backgroundProcess() { // NOOP in base class } @Override public SecurityConstraint[] findSecurityConstraints(Request request, Context context) { ArrayList<SecurityConstraint> results = null; // Are there any defined security constraints? SecurityConstraint[] constraints = context.findConstraints(); if (constraints == null || constraints.length == 0) { if (log.isTraceEnabled()) { log.trace(" No applicable constraints defined"); } return null; } // Check each defined security constraint String uri = request.getRequestPathMB().toString(); // Bug47080 - in rare cases this may be null or "" // Mapper treats as '/' do the same to prevent NPE if (uri == null || uri.isEmpty()) { uri = "/"; } String method = request.getMethod(); int i; boolean found = false; for (i = 0; i < constraints.length; i++) { SecurityCollection[] collections = constraints[i].findCollections(); // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if (collections == null) { continue; } if (log.isTraceEnabled()) { log.trace(" Checking constraint '" + constraints[i] + "' against " + method + " " + uri + " --> " + constraints[i].included(uri, method)); } for (SecurityCollection securityCollection : collections) { String[] patterns = securityCollection.findPatterns(); // If patterns is null, continue to avoid an NPE // See Bugzilla 30624 if (patterns == null) { continue; } for (String pattern : patterns) { // Exact match including special case for the context root. if (uri.equals(pattern) || pattern.isEmpty() && uri.equals("/")) { found = true; if (securityCollection.findMethod(method)) { if (results == null) { results = new ArrayList<>(); } results.add(constraints[i]); } } } } } if (found) { return resultsToArray(results); } int longest = -1; for (i = 0; i < constraints.length; i++) { SecurityCollection[] collection = constraints[i].findCollections(); // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if (collection == null) { continue; } if (log.isTraceEnabled()) { log.trace(" Checking constraint '" + constraints[i] + "' against " + method + " " + uri + " --> " + constraints[i].included(uri, method)); } for (SecurityCollection securityCollection : collection) { String[] patterns = securityCollection.findPatterns(); // If patterns is null, continue to avoid an NPE // See Bugzilla 30624 if (patterns == null) { continue; } boolean matched = false; int length = -1; for (String pattern : patterns) { if (pattern.startsWith("/") && pattern.endsWith("/*") && pattern.length() >= longest) { if (pattern.length() == 2) { matched = true; length = pattern.length(); } else if (pattern.regionMatches(0, uri, 0, pattern.length() - 1) || (pattern.length() - 2 == uri.length() && pattern.regionMatches(0, uri, 0, pattern.length() - 2))) { matched = true; length = pattern.length(); } } } if (matched) { if (length > longest) { found = false; if (results != null) { results.clear(); } longest = length; } if (securityCollection.findMethod(method)) { found = true; if (results == null) { results = new ArrayList<>(); } results.add(constraints[i]); } } } } if (found) { return resultsToArray(results); } for (i = 0; i < constraints.length; i++) { SecurityCollection[] collection = constraints[i].findCollections(); // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if (collection == null) { continue; } if (log.isTraceEnabled()) { log.trace(" Checking constraint '" + constraints[i] + "' against " + method + " " + uri + " --> " + constraints[i].included(uri, method)); } boolean matched = false; int pos = -1; for (int j = 0; j < collection.length; j++) { String[] patterns = collection[j].findPatterns(); // If patterns is null, continue to avoid an NPE // See Bugzilla 30624 if (patterns == null) { continue; } for (int k = 0; k < patterns.length && !matched; k++) { String pattern = patterns[k]; if (pattern.startsWith("*.")) { int slash = uri.lastIndexOf('/'); int dot = uri.lastIndexOf('.'); if (slash >= 0 && dot > slash && dot != uri.length() - 1 && uri.length() - dot == pattern.length() - 1) { if (pattern.regionMatches(1, uri, dot, uri.length() - dot)) { matched = true; pos = j; } } } } } if (matched) { found = true; if (collection[pos].findMethod(method)) { if (results == null) { results = new ArrayList<>(); } results.add(constraints[i]); } } } if (found) { return resultsToArray(results); } for (i = 0; i < constraints.length; i++) { SecurityCollection[] collection = constraints[i].findCollections(); // If collection is null, continue to avoid an NPE // See Bugzilla 30624 if (collection == null) { continue; } if (log.isTraceEnabled()) { log.trace(" Checking constraint '" + constraints[i] + "' against " + method + " " + uri + " --> " + constraints[i].included(uri, method)); } for (SecurityCollection securityCollection : collection) { String[] patterns = securityCollection.findPatterns(); // If patterns is null, continue to avoid an NPE // See Bugzilla 30624 if (patterns == null) { continue; } boolean matched = false; for (String pattern : patterns) { if (pattern.equals("/")) { matched = true; break; } } if (matched) { if (results == null) { results = new ArrayList<>(); } results.add(constraints[i]); } } } if (results == null) { // No applicable security constraint was found if (log.isTraceEnabled()) { log.trace(" No applicable constraint located"); } } return resultsToArray(results); } /** * Convert an ArrayList to a SecurityConstraint []. */ private SecurityConstraint[] resultsToArray(ArrayList<SecurityConstraint> results) { if (results == null || results.isEmpty()) { return null; } return results.toArray(new SecurityConstraint[0]); } @Override public boolean hasResourcePermission(Request request, Response response, SecurityConstraint[] constraints, Context context) throws IOException { if (constraints == null || constraints.length == 0) { return true; } // Which user principal have we already authenticated? Principal principal = request.getPrincipal(); boolean status = false; boolean denyfromall = false; for (SecurityConstraint constraint : constraints) { String[] roles; if (constraint.getAllRoles()) { // * means all roles defined in web.xml roles = request.getContext().findSecurityRoles(); } else { roles = constraint.findAuthRoles(); } if (roles == null) { roles = new String[0]; } if (log.isTraceEnabled()) { log.trace(" Checking roles " + principal); } if (constraint.getAuthenticatedUsers() && principal != null) { if (log.isTraceEnabled()) { log.trace("Passing all authenticated users"); } status = true; } else if (roles.length == 0 && !constraint.getAllRoles() && !constraint.getAuthenticatedUsers()) { if (constraint.getAuthConstraint()) { if (log.isTraceEnabled()) { log.trace("No roles"); } status = false; // No listed roles means no access at all denyfromall = true; break; } if (log.isTraceEnabled()) { log.trace("Passing all access"); } status = true; } else if (principal == null) { if (log.isTraceEnabled()) { log.trace(" No user authenticated, cannot grant access"); } } else { for (String role : roles) { if (hasRole(request.getWrapper(), principal, role)) { status = true; if (log.isTraceEnabled()) { log.trace("Role found: " + role); } } else if (log.isTraceEnabled()) { log.trace("No role found: " + role); } } } } if (!denyfromall && allRolesMode != AllRolesMode.STRICT_MODE && !status && principal != null) { if (log.isTraceEnabled()) { log.trace("Checking for all roles mode: " + allRolesMode); } // Check for an all roles(role-name="*") for (SecurityConstraint constraint : constraints) { // If the all roles mode exists, sets if (constraint.getAllRoles()) { if (allRolesMode == AllRolesMode.AUTH_ONLY_MODE) { if (log.isTraceEnabled()) { log.trace("Granting access for role-name=*, auth-only"); } status = true; break; } // For AllRolesMode.STRICT_AUTH_ONLY_MODE there must be zero roles String[] roles = request.getContext().findSecurityRoles(); if (roles == null) { roles = new String[0]; } if (roles.length == 0 && allRolesMode == AllRolesMode.STRICT_AUTH_ONLY_MODE) { if (log.isTraceEnabled()) { log.trace("Granting access for role-name=*, strict auth-only"); } status = true; break; } } } } // Return a "Forbidden" message denying access to this resource if (!status) { response.sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("realmBase.forbidden")); } return status; } /** * {@inheritDoc} * <p> * This method or {@link #hasRoleInternal(Principal, String)} can be overridden by Realm implementations, but the * default is adequate when an instance of <code>GenericPrincipal</code> is used to represent authenticated * Principals from this Realm. */ @Override public boolean hasRole(Wrapper wrapper, Principal principal, String role) { // Check for a role alias if (wrapper != null) { String realRole = wrapper.findSecurityReference(role); if (realRole != null) { role = realRole; } } // Should be overridden in JAASRealm - to avoid pretty inefficient conversions if (principal == null || role == null) { return false; } boolean result = hasRoleInternal(principal, role); if (log.isTraceEnabled()) { String name = principal.getName(); if (result) { log.trace(sm.getString("realmBase.hasRoleSuccess", name, role)); } else { log.trace(sm.getString("realmBase.hasRoleFailure", name, role)); } } return result; } /** * Parse the specified delimiter separated attribute names and return a list of that names or <code>null</code>, if * no attributes have been specified. * <p> * If a wildcard character is found, return a list consisting of a single wildcard character only. * * @param userAttributes comma separated names of attributes to parse * * @return a list containing the parsed attribute names or <code>null</code>, if no attributes have been specified */ protected List<String> parseUserAttributes(String userAttributes) { if (userAttributes == null) { return null; } List<String> attrs = new ArrayList<>(); for (String name : userAttributes.split(USER_ATTRIBUTES_DELIMITER)) { name = name.trim(); if (name.isEmpty()) { continue; } if (name.equals(USER_ATTRIBUTES_WILDCARD)) { return Collections.singletonList(USER_ATTRIBUTES_WILDCARD); } if (attrs.contains(name)) { // skip duplicates continue; } attrs.add(name); } return !attrs.isEmpty() ? attrs : null; } /** * Check if the specified Principal has the specified security role, within the context of this Realm. This method * or {@link #hasRoleInternal(Principal, String)} can be overridden by Realm implementations, but the default is * adequate when an instance of <code>GenericPrincipal</code> is used to represent authenticated Principals from * this Realm. * * @param principal Principal for whom the role is to be checked * @param role Security role to be checked * * @return <code>true</code> if the specified Principal has the specified security role, within the context of this * Realm; otherwise return <code>false</code>. */ protected boolean hasRoleInternal(Principal principal, String role) { // Should be overridden in JAASRealm - to avoid pretty inefficient conversions if (!(principal instanceof GenericPrincipal gp)) { return false; } return gp.hasRole(role); } @Override public boolean hasUserDataPermission(Request request, Response response, SecurityConstraint[] constraints) throws IOException { // Is there a relevant user data constraint? if (constraints == null || constraints.length == 0) { if (log.isTraceEnabled()) { log.trace(" No applicable security constraint defined"); } return true; } for (SecurityConstraint constraint : constraints) { String userConstraint = constraint.getUserConstraint(); if (userConstraint == null) { if (log.isTraceEnabled()) { log.trace(" No applicable user data constraint defined"); } return true; } if (userConstraint.equals(TransportGuarantee.NONE.name())) { if (log.isTraceEnabled()) { log.trace(" User data constraint has no restrictions"); } return true; } } // Validate the request against the user data constraint if (request.getRequest().isSecure()) { if (log.isTraceEnabled()) { log.trace(" User data constraint already satisfied"); } return true; } // Initialize variables we need to determine the appropriate action int redirectPort = request.getConnector().getRedirectPortWithOffset(); // Is redirecting disabled? if (redirectPort <= 0) { if (log.isTraceEnabled()) { log.trace(" SSL redirect is disabled"); } response.sendError(HttpServletResponse.SC_FORBIDDEN, request.getRequestURI()); return false; } // Redirect to the corresponding SSL port StringBuilder file = new StringBuilder(); String protocol = "https"; String host = request.getServerName(); // Protocol file.append(protocol).append("://").append(host); // Host with port if (redirectPort != 443) { file.append(':').append(redirectPort); } // URI file.append(request.getRequestURI()); String requestedSessionId = request.getRequestedSessionId(); if ((requestedSessionId != null) && request.isRequestedSessionIdFromURL()) { file.append(';'); file.append(SessionConfig.getSessionUriParamName(request.getContext())); file.append('='); file.append(requestedSessionId); } String queryString = request.getQueryString(); if (queryString != null) { file.append('?'); file.append(queryString); } if (log.isTraceEnabled()) { log.trace(" Redirecting to " + file.toString()); } response.sendRedirect(file.toString(), transportGuaranteeRedirectStatus); return false; } @Override public void removePropertyChangeListener(PropertyChangeListener listener) { support.removePropertyChangeListener(listener); } @Override protected void initInternal() throws LifecycleException { super.initInternal(); // We want logger as soon as possible if (container != null) { this.containerLog = container.getLogger(); } x509UsernameRetriever = createUsernameRetriever(x509UsernameRetrieverClassName); } /** * Prepare for the beginning of active use of the public methods of this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error that prevents this component from being * used */ @Override protected void startInternal() throws LifecycleException { if (credentialHandler == null) { credentialHandler = new MessageDigestCredentialHandler(); } if (userAttributes != null) { userAttributesList = parseUserAttributes(userAttributes); } setState(LifecycleState.STARTING); } /** * Gracefully terminate the active use of the public methods of this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error that needs to be reported */ @Override protected void stopInternal() throws LifecycleException { setState(LifecycleState.STOPPING); } @Override public String toString() { return ToStringUtil.toString(this); } // ------------------------------------------------------ Protected Methods protected boolean hasMessageDigest(String algorithm) { CredentialHandler ch = credentialHandler; if (ch instanceof MessageDigestCredentialHandler) { String realmAlgorithm = ((MessageDigestCredentialHandler) ch).getAlgorithm(); if (realmAlgorithm != null) { if (realmAlgorithm.equals(algorithm)) { return true; } else { log.debug(sm.getString("relamBase.digestMismatch", algorithm, realmAlgorithm)); } } } return false; } /** * Return the digest associated with given principal's username. * * @param username The username * @param realmName The realm name * @param algorithm The name of the message digest algorithm to use * * @return the digest for the specified user */ protected String getDigest(String username, String realmName, String algorithm) { if (hasMessageDigest(algorithm)) { // Use pre-generated digest return getPassword(username); } String digestValue = username + ":" + realmName + ":" + getPassword(username); byte[] valueBytes; try { valueBytes = digestValue.getBytes(getDigestCharset()); } catch (UnsupportedEncodingException uee) { throw new IllegalArgumentException(sm.getString("realmBase.invalidDigestEncoding", getDigestEncoding()), uee); } return HexUtils.toHexString(ConcurrentMessageDigest.digest(algorithm, valueBytes)); } private String getDigestEncoding() { CredentialHandler ch = credentialHandler; if (ch instanceof MessageDigestCredentialHandler) { return ((MessageDigestCredentialHandler) ch).getEncoding(); } return null; } private Charset getDigestCharset() throws UnsupportedEncodingException { String charset = getDigestEncoding(); if (charset == null) { return StandardCharsets.ISO_8859_1; } else { return B2CConverter.getCharset(charset); } } /** * Get the password for the specified user. * * @param username The username * * @return the password associated with the given principal's username. */ protected abstract String getPassword(String username); /** * Get the principal associated with the specified certificate. * * @param usercert The user certificate * * @return the Principal associated with the given certificate. */ protected Principal getPrincipal(X509Certificate usercert) { String username = x509UsernameRetriever.getUsername(usercert); if (log.isTraceEnabled()) { log.trace(sm.getString("realmBase.gotX509Username", username)); } return getPrincipal(username); } /** * Get the principal associated with the specified user. * * @param username The username * * @return the Principal associated with the given username. */ protected abstract Principal getPrincipal(String username); /** * Get the principal associated with the specified {@link GSSName}. * * @param gssName The GSS name * @param gssCredential the GSS credential of the principal * @param gssContext the established GSS context * * @return the principal associated with the given username. */ protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential, GSSContext gssContext) { return getPrincipal(gssName, gssCredential); } /** * Get the principal associated with the specified {@link GSSName}. * * @param gssName The GSS name * @param gssCredential the GSS credential of the principal * * @return the principal associated with the given username. */ protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential) { String name = gssName.toString(); if (isStripRealmForGss()) { int i = name.indexOf('@'); if (i > 0) { // Zero so we don't leave a zero length name name = name.substring(0, i); } } Principal p = getPrincipal(name); if (p instanceof GenericPrincipal) { ((GenericPrincipal) p).setGssCredential(gssCredential); } return p; } /** * Return the Server object that is the ultimate parent for the container with which this Realm is associated. If * the server cannot be found (e.g. because the container hierarchy is not complete), <code>null</code> is returned. * * @return the Server associated with the realm */ protected Server getServer() { Container c = container; if (c instanceof Context) { c = c.getParent(); } if (c instanceof Host) { c = c.getParent(); } if (c instanceof Engine) { Service s = ((Engine) c).getService(); if (s != null) { return s.getServer(); } } return null; } // --------------------------------------------------------- Static Methods /** * Generate a stored credential string for the given password and associated parameters. * <p> * The following parameters are supported: * </p> * <ul> * <li><b>-a</b> - The algorithm to use to generate the stored credential. If not specified a default of SHA-512 * will be used.</li> * <li><b>-e</b> - The encoding to use for any byte to/from character conversion that may be necessary. If not * specified, the system encoding ({@link Charset#defaultCharset()}) will be used.</li> * <li><b>-i</b> - The number of iterations to use when generating the stored credential. If not specified, the * default for the CredentialHandler will be used.</li> * <li><b>-s</b> - The length (in bytes) of salt to generate and store as part of the credential. If not specified, * the default for the CredentialHandler will be used.</li> * <li><b>-k</b> - The length (in bits) of the key(s), if any, created while generating the credential. If not * specified, the default for the CredentialHandler will be used.</li> * <li><b>-h</b> - The fully qualified class name of the CredentialHandler to use. If not specified, the built-in * handlers will be tested in turn and the first one to accept the specified algorithm will be used.</li> * <li><b>-f</b> - The name of the file that contains passwords to encode. Each line in the file should contain only * one password. Using this option ignores other password input.</li> * </ul> * <p> * This generation process currently supports the following CredentialHandlers, the correct one being selected based * on the algorithm specified: * </p> * <ul> * <li>{@link MessageDigestCredentialHandler}</li> * <li>{@link SecretKeyCredentialHandler}</li> * </ul> * * @param args The parameters passed on the command line * * @throws IOException If an error occurs reading the password file */ public static void main(String[] args) throws IOException { // Use negative values since null is not an option to indicate 'not set' int saltLength = -1; int iterations = -1; int keyLength = -1; // Default String encoding = Charset.defaultCharset().name(); // Default values for these depend on whether either of them are set on // the command line String algorithm = null; String handlerClassName = null; // File name to read password(s) from String passwordFile = null; if (args.length == 0) { usage(); return; } int argIndex = 0; // Boolean to check and see if we've reached the -- option boolean endOfList = false; // Note: Reducing args.length requirement to argIndex+1 so that -f works and ignores // trailing words while (args.length > argIndex + 1 && args[argIndex].length() == 2 && args[argIndex].charAt(0) == '-' && !endOfList) { switch (args[argIndex].charAt(1)) { case 'a': { algorithm = args[argIndex + 1]; break; } case 'e': { encoding = args[argIndex + 1]; break; } case 'i': { iterations = Integer.parseInt(args[argIndex + 1]); break; } case 's': { saltLength = Integer.parseInt(args[argIndex + 1]); break; } case 'k': { keyLength = Integer.parseInt(args[argIndex + 1]); break; } case 'h': { handlerClassName = args[argIndex + 1]; break; } case 'f': { passwordFile = args[argIndex + 1]; break; } case '-': { // When encountering -- option don't parse anything else as an option endOfList = true; // The -- opt doesn't take an argument, decrement the argIndex so that it parses // all remaining args argIndex--; break; } default: { usage(); return; } } argIndex += 2; } // Determine defaults for -a and -h. The rules are more complex to // express than the implementation: // - if neither -a nor -h is set, use SHA-512 and // MessageDigestCredentialHandler // - if only -a is set the built-in handlers will be searched in order // (MessageDigestCredentialHandler, SecretKeyCredentialHandler) and // the first handler that supports the algorithm will be used // - if only -h is set no default will be used for -a. The handler may // or may nor support -a and may or may not supply a sensible default if (algorithm == null && handlerClassName == null) { algorithm = "SHA-512"; } CredentialHandler handler = null; if (handlerClassName == null) { for (Class<? extends DigestCredentialHandlerBase> clazz : credentialHandlerClasses) { try { handler = clazz.getConstructor().newInstance(); if (IntrospectionUtils.setProperty(handler, "algorithm", algorithm)) { break; } } catch (ReflectiveOperationException e) { // This isn't good. throw new RuntimeException(e); } } } else { try { Class<?> clazz = Class.forName(handlerClassName); handler = (DigestCredentialHandlerBase) clazz.getConstructor().newInstance(); IntrospectionUtils.setProperty(handler, "algorithm", algorithm); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } if (handler == null) { throw new RuntimeException(new NoSuchAlgorithmException(algorithm)); } IntrospectionUtils.setProperty(handler, "encoding", encoding); if (iterations > 0) { IntrospectionUtils.setProperty(handler, "iterations", Integer.toString(iterations)); } if (saltLength > -1) { IntrospectionUtils.setProperty(handler, "saltLength", Integer.toString(saltLength)); } if (keyLength > 0) { IntrospectionUtils.setProperty(handler, "keyLength", Integer.toString(keyLength)); } if (passwordFile != null) { // If the file name is used, then don't parse the trailing arguments argIndex = args.length; // Special case, allow for - filename to refer to stdin try (BufferedReader br = passwordFile.equals("-") ? new BufferedReader(new InputStreamReader(System.in)) : new BufferedReader(new FileReader(passwordFile))) { String line; while ((line = br.readLine()) != null) { // Mutate each line in the file, or stdin mutateCredential(line, handler); } } catch (Exception e) { // A FileNotFound is the likely exception here and self-explanatory. Softly // reporting it and exit 1 so that you can tell it failed from the command line. if (e instanceof java.io.FileNotFoundException) { System.err.println("cannot stat '" + passwordFile + "': No such file or directory"); // Not sure if using an exit here is OK, but I wanted to return a code that // showed failure. System.exit(1); } else { throw e; } } } for (; argIndex < args.length; argIndex++) { mutateCredential(args[argIndex], handler); } } private static void mutateCredential(String credential, CredentialHandler handler) { System.out.print(credential + ":"); System.out.println(handler.mutate(credential)); } private static void usage() { System.out.println("Usage: RealmBase [-a <algorithm>] [-e <encoding>]" + " [-i <iterations>] [-s <salt-length>] [-k <key-length>]" + " [-h <handler-class-name>] | <XX credentials>"); } // -------------------- JMX and Registration -------------------- @Override public String getObjectNameKeyProperties() { return "type=Realm" + getRealmSuffix() + container.getMBeanKeyProperties(); } @Override public String getDomainInternal() { return container.getDomain(); } protected String realmPath = "/realm0"; public String getRealmPath() { return realmPath; } public void setRealmPath(String theRealmPath) { realmPath = theRealmPath; } protected String getRealmSuffix() { return ",realmPath=" + getRealmPath(); } protected static class AllRolesMode { private final String name; /** * Use the strict servlet spec interpretation which requires that the user have one of the * web-app/security-role/role-name */ public static final AllRolesMode STRICT_MODE = new AllRolesMode("strict"); /** Allow any authenticated user */ public static final AllRolesMode AUTH_ONLY_MODE = new AllRolesMode("authOnly"); /** * Allow any authenticated user only if there are no web-app/security-roles */ public static final AllRolesMode STRICT_AUTH_ONLY_MODE = new AllRolesMode("strictAuthOnly"); static AllRolesMode toMode(String name) { AllRolesMode mode; if (name.equalsIgnoreCase(STRICT_MODE.name)) { mode = STRICT_MODE; } else if (name.equalsIgnoreCase(AUTH_ONLY_MODE.name)) { mode = AUTH_ONLY_MODE; } else if (name.equalsIgnoreCase(STRICT_AUTH_ONLY_MODE.name)) { mode = STRICT_AUTH_ONLY_MODE; } else { throw new IllegalStateException(sm.getString("realmBase.unknownAllRolesMode", name)); } return mode; } private AllRolesMode(String name) { this.name = name; } @Override public boolean equals(Object o) { boolean equals = false; if (o instanceof AllRolesMode mode) { equals = name.equals(mode.name); } return equals; } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return name; } } private static X509UsernameRetriever createUsernameRetriever(String className) throws LifecycleException { if (null == className || className.trim().isEmpty()) { return new X509SubjectDnRetriever(); } try { @SuppressWarnings("unchecked") Class<? extends X509UsernameRetriever> clazz = (Class<? extends X509UsernameRetriever>) Class.forName(className); return clazz.getConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new LifecycleException(sm.getString("realmBase.createUsernameRetriever.newInstance", className), e); } catch (ClassCastException e) { throw new LifecycleException( sm.getString("realmBase.createUsernameRetriever.ClassCastException", className), e); } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
2.43
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9