Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP5:Update
tomcat.26946
tomcat-8.0.53-CVE-2021-30640.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File tomcat-8.0.53-CVE-2021-30640.patch of Package tomcat.26946
Index: apache-tomcat-8.0.53-src/java/org/apache/catalina/realm/JNDIRealm.java =================================================================== --- apache-tomcat-8.0.53-src.orig/java/org/apache/catalina/realm/JNDIRealm.java +++ apache-tomcat-8.0.53-src/java/org/apache/catalina/realm/JNDIRealm.java @@ -1626,8 +1626,11 @@ public class JNDIRealm extends RealmBase if (username == null || userPatternFormatArray[curUserPattern] == null) return null; - // Form the dn from the user pattern - String dn = userPatternFormatArray[curUserPattern].format(new String[] { username }); + // Form the DistinguishedName from the user pattern. + // Escape in case username contains a character with special meaning in + // an attribute value. + String dn = userPatternFormatArray[curUserPattern].format( + new String[] { doAttributeValueEscaping(username) }); try { user = getUserByPattern(context, username, attrIds, dn); @@ -1668,7 +1671,9 @@ public class JNDIRealm extends RealmBase return null; // Form the search filter - String filter = userSearchFormat.format(new String[] { username }); + // Escape in case username contains a character with special meaning in + // a search filter. + String filter = userSearchFormat.format(new String[] { doFilterEscaping(username) }); // Set up the search controls SearchControls constraints = new SearchControls(); @@ -1959,8 +1964,13 @@ public class JNDIRealm extends RealmBase if ((roleFormat == null) || (roleName == null)) return list; - // Set up parameters for an appropriate search - String filter = roleFormat.format(new String[] { doRFC2254Encoding(dn), username, userRoleId }); + // Set up parameters for an appropriate search filter + // The dn is already attribute value escaped but the others are not + // This is a filter so all input will require filter escaping + String filter = roleFormat.format(new String[] { + doFilterEscaping(dn), + doFilterEscaping(doAttributeValueEscaping(username)), + doFilterEscaping(doAttributeValueEscaping(userRoleId)) }); SearchControls controls = new SearchControls(); if (roleSubtree) controls.setSearchScope(SearchControls.SUBTREE_SCOPE); @@ -1974,7 +1984,9 @@ public class JNDIRealm extends RealmBase Name name = np.parse(dn); String nameParts[] = new String[name.size()]; for (int i = 0; i < name.size(); i++) { - nameParts[i] = name.get(i); + // May have been returned with \<char> escaping rather than + // \<hex><hex>. Make sure it is \<hex><hex>. + nameParts[i] = convertToHexEscape(name.get(i)); } base = roleBaseFormat.format(nameParts); } else { @@ -1995,7 +2007,7 @@ public class JNDIRealm extends RealmBase Attributes attrs = result.getAttributes(); if (attrs == null) continue; - String dname = getDistinguishedName(context, roleBase, result); + String dname = getDistinguishedName(context, base, result); String name = getAttributeValue(roleName, attrs); if (name != null && dname != null) { groupMap.put(dname, name); @@ -2028,14 +2040,19 @@ public class JNDIRealm extends RealmBase Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration for (Entry<String, String> group : newGroups.entrySet()) { - filter = roleFormat.format(new String[] { group.getKey(), group.getValue(), group.getValue() }); + // Group key is already value escaped if required + // Group value is not value escaped + // Everything needs to be filter escaped + filter = roleFormat.format(new String[] { + doFilterEscaping(group.getKey()), + doFilterEscaping(doAttributeValueEscaping(group.getValue())), + doFilterEscaping(doAttributeValueEscaping(group.getValue())) }); if (containerLog.isTraceEnabled()) { - containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter); + containerLog.trace("Perform a nested group search with base "+ roleBase + + " and filter " + filter); } - - results = searchAsUser(context, user, roleBase, filter, controls, - isRoleSearchAsUser()); + results = searchAsUser(context, user, base, filter, controls, isRoleSearchAsUser()); try { while (results.hasMore()) { @@ -2477,12 +2494,14 @@ public class JNDIRealm extends RealmBase sslContext = SSLContext.getDefault(); } return sslContext.getSocketFactory(); - } catch (NoSuchAlgorithmException | KeyManagementException e) { - List<String> allowedProtocols = Arrays - .asList(getSupportedSslProtocols()); - throw new IllegalArgumentException( - sm.getString("jndiRealm.invalidSslProtocol", protocol, - allowedProtocols), e); + } catch (NoSuchAlgorithmException e) { + List<String> allowedProtocols = Arrays.asList(getSupportedSslProtocols()); + throw new IllegalArgumentException(sm.getString("jndiRealm.invalidSslProtocol", + protocol, allowedProtocols), e); + } catch (KeyManagementException e) { + List<String> allowedProtocols = Arrays.asList(getSupportedSslProtocols()); + throw new IllegalArgumentException(sm.getString("jndiRealm.invalidSslProtocol", + protocol, allowedProtocols), e); } } @@ -2698,6 +2717,27 @@ public class JNDIRealm extends RealmBase * @return String the escaped/encoded result */ protected String doRFC2254Encoding(String inString) { + return doFilterEscaping(inString); + } + + + /** + * Given an LDAP search string, returns the string with certain characters + * escaped according to RFC 2254 guidelines. + * The character mapping is as follows: + * char -> Replacement + * --------------------------- + * * -> \2a + * ( -> \28 + * ) -> \29 + * \ -> \5c + * \0 -> \00 + * + * @param inString string to escape according to RFC 2254 guidelines + * + * @return String the escaped/encoded result + */ + protected String doFilterEscaping(String inString) { StringBuilder buf = new StringBuilder(inString.length()); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); @@ -2782,6 +2822,148 @@ public class JNDIRealm extends RealmBase } } + /** + * Implements the necessary escaping to represent an attribute value as a + * String as per RFC 4514. + * + * @param input The original attribute value + * @return The string representation of the attribute value + */ + protected String doAttributeValueEscaping(String input) { + int len = input.length(); + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < len; i++) { + char c = input.charAt(i); + switch (c) { + case ' ': { + if (i == 0 || i == (len -1)) { + result.append("\\20"); + } else { + result.append(c); + } + break; + } + case '#': { + if (i == 0 ) { + result.append("\\23"); + } else { + result.append(c); + } + break; + } + case '\"': { + result.append("\\22"); + break; + } + case '+': { + result.append("\\2B"); + break; + } + case ',': { + result.append("\\2C"); + break; + } + case ';': { + result.append("\\3B"); + break; + } + case '<': { + result.append("\\3C"); + break; + } + case '>': { + result.append("\\3E"); + break; + } + case '\\': { + result.append("\\5C"); + break; + } + case '\u0000': { + result.append("\\00"); + break; + } + default: + result.append(c); + } + + } + + return result.toString(); + } + + + protected static String convertToHexEscape(String input) { + if (input.indexOf('\\') == -1) { + // No escaping present. Return original. + return input; + } + // +6 allows for 3 escaped characters by default + StringBuilder result = new StringBuilder(input.length() + 6); + boolean previousSlash = false; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (previousSlash) { + switch (c) { + case ' ': { + result.append("\\20"); + break; + } + case '\"': { + result.append("\\22"); + break; + } + case '#': { + result.append("\\23"); + break; + } + case '+': { + result.append("\\2B"); + break; + } + case ',': { + result.append("\\2C"); + break; + } + case ';': { + result.append("\\3B"); + break; + } + case '<': { + result.append("\\3C"); + break; + } + case '=': { + result.append("\\3D"); + break; + } + case '>': { + result.append("\\3E"); + break; + } + case '\\': { + result.append("\\5C"); + break; + } + default: + result.append('\\'); + result.append(c); + } + previousSlash = false; + } else { + if (c == '\\') { + previousSlash = true; + } else { + result.append(c); + } + } + } + if (previousSlash) { + result.append('\\'); + } + return result.toString(); + } // ------------------------------------------------------ Private Classes Index: apache-tomcat-8.0.53-src/webapps/docs/changelog.xml =================================================================== --- apache-tomcat-8.0.53-src.orig/webapps/docs/changelog.xml +++ apache-tomcat-8.0.53-src/webapps/docs/changelog.xml @@ -146,6 +146,10 @@ set using a canonical path which in turn meant resource URLs were not being constructed as expected. (markt) </fix> + <fix> + <bug>65224</bug>: Ensure the correct escaping of attribute values and + search filters in the JNDIRealm. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote">
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor