Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP4:Update
tomcat.28268
tomcat-9.0-CVE-2021-30640.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File tomcat-9.0-CVE-2021-30640.patch of Package tomcat.28268
Index: apache-tomcat-9.0.43-src/build.properties.default =================================================================== --- apache-tomcat-9.0.43-src.orig/build.properties.default +++ apache-tomcat-9.0.43-src/build.properties.default @@ -244,6 +244,15 @@ objenesis.home=${base.path}/objenesis-${ objenesis.jar=${objenesis.home}/objenesis-${objenesis.version}.jar objenesis.loc=${base-maven.loc}/org/objenesis/objenesis/${objenesis.version}/objenesis-${objenesis.version}.jar +# ----- UnboundID, used by unit tests, version 5.1.4 or later ----- +unboundid.version=5.1.4 +unboundid.checksum.enabled=true +unboundid.checksum.algorithm=SHA-512 +unboundid.checksum.value=04cf7f59eddebdd5b51e5be55021f9d9c667cca6101eac954e7a8d5b51f4c23372cd8f041640157f082435a166b75d85e79252b516130ede7d966dae6d3eae67 +unboundid.home=${base.path}/unboundid-${unboundid.version} +unboundid.jar=${unboundid.home}/unboundid-ldapsdk-${unboundid.version}.jar +unboundid.loc=${base-maven.loc}/com/unboundid/unboundid-ldapsdk/${unboundid.version}/unboundid-ldapsdk-${unboundid.version}.jar + # ----- Checkstyle, version 6.16 or later ----- checkstyle.version=8.22 checkstyle.checksum.enabled=true Index: apache-tomcat-9.0.43-src/build.xml =================================================================== --- apache-tomcat-9.0.43-src.orig/build.xml +++ apache-tomcat-9.0.43-src/build.xml @@ -3164,6 +3164,15 @@ skip.installer property in build.propert <param name="checksum.value" value="${objenesis.checksum.value}"/> </antcall> + <antcall target="downloadfile"> + <param name="sourcefile" value="${unboundid.loc}"/> + <param name="destfile" value="${unboundid.jar}"/> + <param name="destdir" value="${unboundid.home}"/> + <param name="checksum.enabled" value="${unboundid.checksum.enabled}"/> + <param name="checksum.algorithm" value="${unboundid.checksum.algorithm}"/> + <param name="checksum.value" value="${unboundid.checksum.value}"/> + </antcall> + </target> <target name="download-cobertura" Index: apache-tomcat-9.0.43-src/java/org/apache/catalina/realm/JNDIRealm.java =================================================================== --- apache-tomcat-9.0.43-src.orig/java/org/apache/catalina/realm/JNDIRealm.java +++ apache-tomcat-9.0.43-src/java/org/apache/catalina/realm/JNDIRealm.java @@ -181,7 +181,6 @@ import org.ietf.jgss.GSSName; */ public class JNDIRealm extends RealmBase { - // ----------------------------------------------------- Instance Variables /** @@ -194,19 +193,16 @@ public class JNDIRealm extends RealmBase */ protected String connectionName = null; - /** * The connection password for the server we will contact. */ protected String connectionPassword = null; - /** * The connection URL for the server we will contact. */ protected String connectionURL = null; - /** * The JNDI context factory used to acquire our InitialContext. By * default, assumes use of an LDAP server using the standard JNDI LDAP @@ -214,7 +210,6 @@ public class JNDIRealm extends RealmBase */ protected String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; - /** * How aliases should be dereferenced during search operations. */ @@ -226,14 +221,12 @@ public class JNDIRealm extends RealmBase */ public static final String DEREF_ALIASES = "java.naming.ldap.derefAliases"; - /** * The protocol that will be used in the communication with the * directory server. */ protected String protocol = null; - /** * Should we ignore PartialResultExceptions when iterating over NamingEnumerations? * Microsoft Active Directory often returns referrals, which lead @@ -243,7 +236,6 @@ public class JNDIRealm extends RealmBase */ protected boolean adCompat = false; - /** * How should we handle referrals? Microsoft Active Directory often returns * referrals. If you need to follow them set referrals to "follow". @@ -252,20 +244,17 @@ public class JNDIRealm extends RealmBase */ protected String referrals = null; - /** * The base element for user searches. */ protected String userBase = ""; - /** * The message format used to search for a user, with "{0}" marking * the spot where the username goes. */ protected String userSearch = null; - /** * When searching for users, should the search be performed as the user * currently being authenticated? If false, {@link #connectionName} and @@ -274,13 +263,11 @@ public class JNDIRealm extends RealmBase */ private boolean userSearchAsUser = false; - /** * Should we search the entire subtree for matching users? */ protected boolean userSubtree = false; - /** * The attribute name used to retrieve the user password. */ @@ -294,7 +281,6 @@ public class JNDIRealm extends RealmBase */ protected String userRoleAttribute = null; - /** * A string of LDAP user patterns or paths, ":"-separated * These will be used to form the distinguished name of a @@ -305,7 +291,6 @@ public class JNDIRealm extends RealmBase */ protected String[] userPatternArray = null; - /** * The message format used to form the distinguished name of a * user, with "{0}" marking the spot where the specified username @@ -313,26 +298,22 @@ public class JNDIRealm extends RealmBase */ protected String userPattern = null; - /** * The base element for role searches. */ protected String roleBase = ""; - /** * The name of an attribute in the user's entry containing * roles for that user */ protected String userRoleName = null; - /** * The name of the attribute containing roles held elsewhere */ protected String roleName = null; - /** * The message format used to select roles for a user, with "{0}" marking * the spot where the distinguished name of the user goes. The "{1}" @@ -340,7 +321,6 @@ public class JNDIRealm extends RealmBase */ protected String roleSearch = null; - /** * Should we search the entire subtree for matching memberships? */ @@ -375,7 +355,6 @@ public class JNDIRealm extends RealmBase */ protected String commonRole = null; - /** * The timeout, in milliseconds, to use when trying to create a connection * to the directory. The default is 5000 (5 seconds). @@ -400,14 +379,12 @@ public class JNDIRealm extends RealmBase */ protected int timeLimit = 0; - /** * Should delegated credentials from the SPNEGO authenticator be used if * available */ protected boolean useDelegatedCredential = true; - /** * The QOP that should be used for the connection to the LDAP server after * authentication. This value is used to set the @@ -465,31 +442,26 @@ public class JNDIRealm extends RealmBase private boolean forceDnHexEscape = false; - /** * Non pooled connection to our directory server. */ protected JNDIConnection singleConnection = new JNDIConnection(); - /** * The lock to ensure single connection thread safety. */ protected final Lock singleConnectionLock = new ReentrantLock(); - /** * Connection pool. */ protected SynchronizedStack<JNDIConnection> connectionPool = null; - /** * The pool size limit. If 1, pooling is not used. */ protected int connectionPoolSize = 1; - /** * Whether to use context ClassLoader or default ClassLoader. * True means use context ClassLoader, and True is the default @@ -504,37 +476,35 @@ public class JNDIRealm extends RealmBase return forceDnHexEscape; } + public void setForceDnHexEscape(boolean forceDnHexEscape) { this.forceDnHexEscape = forceDnHexEscape; } + /** * @return the type of authentication to use. */ public String getAuthentication() { - return authentication; - } + /** * Set the type of authentication to use. * * @param authentication The authentication */ public void setAuthentication(String authentication) { - this.authentication = authentication; - } + /** * @return the connection username for this Realm. */ public String getConnectionName() { - return this.connectionName; - } @@ -544,9 +514,7 @@ public class JNDIRealm extends RealmBase * @param connectionName The new connection username */ public void setConnectionName(String connectionName) { - this.connectionName = connectionName; - } @@ -554,9 +522,7 @@ public class JNDIRealm extends RealmBase * @return the connection password for this Realm. */ public String getConnectionPassword() { - return this.connectionPassword; - } @@ -566,9 +532,7 @@ public class JNDIRealm extends RealmBase * @param connectionPassword The new connection password */ public void setConnectionPassword(String connectionPassword) { - this.connectionPassword = connectionPassword; - } @@ -576,9 +540,7 @@ public class JNDIRealm extends RealmBase * @return the connection URL for this Realm. */ public String getConnectionURL() { - return this.connectionURL; - } @@ -588,9 +550,7 @@ public class JNDIRealm extends RealmBase * @param connectionURL The new connection URL */ public void setConnectionURL(String connectionURL) { - this.connectionURL = connectionURL; - } @@ -598,9 +558,7 @@ public class JNDIRealm extends RealmBase * @return the JNDI context factory for this Realm. */ public String getContextFactory() { - return this.contextFactory; - } @@ -610,11 +568,10 @@ public class JNDIRealm extends RealmBase * @param contextFactory The new context factory */ public void setContextFactory(String contextFactory) { - this.contextFactory = contextFactory; - } + /** * @return the derefAliases setting to be used. */ @@ -622,33 +579,32 @@ public class JNDIRealm extends RealmBase return derefAliases; } + /** * Set the value for derefAliases to be used when searching the directory. * * @param derefAliases New value of property derefAliases. */ public void setDerefAliases(java.lang.String derefAliases) { - this.derefAliases = derefAliases; + this.derefAliases = derefAliases; } + /** * @return the protocol to be used. */ public String getProtocol() { - return protocol; - } + /** * Set the protocol for this Realm. * * @param protocol The new protocol. */ public void setProtocol(String protocol) { - this.protocol = protocol; - } @@ -692,9 +648,7 @@ public class JNDIRealm extends RealmBase * @return the base element for user searches. */ public String getUserBase() { - return this.userBase; - } @@ -704,9 +658,7 @@ public class JNDIRealm extends RealmBase * @param userBase The new base element */ public void setUserBase(String userBase) { - this.userBase = userBase; - } @@ -714,9 +666,7 @@ public class JNDIRealm extends RealmBase * @return the message format pattern for selecting users in this Realm. */ public String getUserSearch() { - return this.userSearch; - } @@ -745,9 +695,7 @@ public class JNDIRealm extends RealmBase * @return the "search subtree for users" flag. */ public boolean getUserSubtree() { - return this.userSubtree; - } @@ -757,9 +705,7 @@ public class JNDIRealm extends RealmBase * @param userSubtree The new search flag */ public void setUserSubtree(boolean userSubtree) { - this.userSubtree = userSubtree; - } @@ -767,7 +713,6 @@ public class JNDIRealm extends RealmBase * @return the user role name attribute name for this Realm. */ public String getUserRoleName() { - return userRoleName; } @@ -778,9 +723,7 @@ public class JNDIRealm extends RealmBase * @param userRoleName The new userRole name attribute name */ public void setUserRoleName(String userRoleName) { - this.userRoleName = userRoleName; - } @@ -788,9 +731,7 @@ public class JNDIRealm extends RealmBase * @return the base element for role searches. */ public String getRoleBase() { - return this.roleBase; - } @@ -809,9 +750,7 @@ public class JNDIRealm extends RealmBase * @return the role name attribute name for this Realm. */ public String getRoleName() { - return this.roleName; - } @@ -821,9 +760,7 @@ public class JNDIRealm extends RealmBase * @param roleName The new role name attribute name */ public void setRoleName(String roleName) { - this.roleName = roleName; - } @@ -831,9 +768,7 @@ public class JNDIRealm extends RealmBase * @return the message format pattern for selecting roles in this Realm. */ public String getRoleSearch() { - return this.roleSearch; - } @@ -862,9 +797,7 @@ public class JNDIRealm extends RealmBase * @return the "search subtree for roles" flag. */ public boolean getRoleSubtree() { - return this.roleSubtree; - } @@ -874,18 +807,15 @@ public class JNDIRealm extends RealmBase * @param roleSubtree The new search flag */ public void setRoleSubtree(boolean roleSubtree) { - this.roleSubtree = roleSubtree; - } + /** * @return the "The nested group search flag" flag. */ public boolean getRoleNested() { - return this.roleNested; - } @@ -895,9 +825,7 @@ public class JNDIRealm extends RealmBase * @param roleNested The nested group search flag */ public void setRoleNested(boolean roleNested) { - this.roleNested = roleNested; - } @@ -905,9 +833,7 @@ public class JNDIRealm extends RealmBase * @return the password attribute used to retrieve the user password. */ public String getUserPassword() { - return this.userPassword; - } @@ -917,9 +843,7 @@ public class JNDIRealm extends RealmBase * @param userPassword The new password attribute */ public void setUserPassword(String userPassword) { - this.userPassword = userPassword; - } @@ -927,6 +851,7 @@ public class JNDIRealm extends RealmBase return userRoleAttribute; } + public void setUserRoleAttribute(String userRoleAttribute) { this.userRoleAttribute = userRoleAttribute; } @@ -935,14 +860,10 @@ public class JNDIRealm extends RealmBase * @return the message format pattern for selecting users in this Realm. */ public String getUserPattern() { - return this.userPattern; - } - - /** * Set the message format pattern for selecting users in this Realm. * This may be one simple pattern, or multiple patterns to be tried, @@ -970,9 +891,7 @@ public class JNDIRealm extends RealmBase * @return Value of property alternateURL. */ public String getAlternateURL() { - return this.alternateURL; - } @@ -982,9 +901,7 @@ public class JNDIRealm extends RealmBase * @param alternateURL New value of property alternateURL. */ public void setAlternateURL(String alternateURL) { - this.alternateURL = alternateURL; - } @@ -992,9 +909,7 @@ public class JNDIRealm extends RealmBase * @return the common role */ public String getCommonRole() { - return commonRole; - } @@ -1004,9 +919,7 @@ public class JNDIRealm extends RealmBase * @param commonRole The common role */ public void setCommonRole(String commonRole) { - this.commonRole = commonRole; - } @@ -1014,9 +927,7 @@ public class JNDIRealm extends RealmBase * @return the connection timeout. */ public String getConnectionTimeout() { - return connectionTimeout; - } @@ -1026,18 +937,15 @@ public class JNDIRealm extends RealmBase * @param timeout The new connection timeout */ public void setConnectionTimeout(String timeout) { - this.connectionTimeout = timeout; - } + /** * @return the read timeout. */ public String getReadTimeout() { - return readTimeout; - } @@ -1047,9 +955,7 @@ public class JNDIRealm extends RealmBase * @param timeout The new read timeout */ public void setReadTimeout(String timeout) { - this.readTimeout = timeout; - } @@ -1077,6 +983,7 @@ public class JNDIRealm extends RealmBase return useDelegatedCredential; } + public void setUseDelegatedCredential(boolean useDelegatedCredential) { this.useDelegatedCredential = useDelegatedCredential; } @@ -1086,6 +993,7 @@ public class JNDIRealm extends RealmBase return spnegoDelegationQop; } + public void setSpnegoDelegationQop(String spnegoDelegationQop) { this.spnegoDelegationQop = spnegoDelegationQop; } @@ -1098,6 +1006,7 @@ public class JNDIRealm extends RealmBase return useStartTls; } + /** * Flag whether StartTLS should be used when connecting to the ldap server * @@ -1109,6 +1018,7 @@ public class JNDIRealm extends RealmBase this.useStartTls = useStartTls; } + /** * @return list of the allowed cipher suites when connections are made using * StartTLS @@ -1128,6 +1038,7 @@ public class JNDIRealm extends RealmBase return this.cipherSuitesArray; } + /** * Set the allowed cipher suites when opening a connection using StartTLS. * The cipher suites are expected as a comma separated list. @@ -1139,6 +1050,7 @@ public class JNDIRealm extends RealmBase this.cipherSuites = suites; } + /** * @return the connection pool size, or the default value 1 if pooling * is disabled @@ -1147,6 +1059,7 @@ public class JNDIRealm extends RealmBase return connectionPoolSize; } + /** * Set the connection pool size * @param connectionPoolSize the new pool size @@ -1155,6 +1068,7 @@ public class JNDIRealm extends RealmBase this.connectionPoolSize = connectionPoolSize; } + /** * @return name of the {@link HostnameVerifier} class used for connections * using StartTLS, or the empty string, if the default verifier @@ -1167,6 +1081,7 @@ public class JNDIRealm extends RealmBase return this.hostnameVerifier.getClass().getCanonicalName(); } + /** * Set the {@link HostnameVerifier} to be used when opening connections * using StartTLS. An instance of the given class name will be constructed @@ -1183,6 +1098,7 @@ public class JNDIRealm extends RealmBase } } + /** * @return the {@link HostnameVerifier} to use for peer certificate * verification when opening connections using StartTLS. @@ -1191,8 +1107,7 @@ public class JNDIRealm extends RealmBase if (this.hostnameVerifier != null) { return this.hostnameVerifier; } - if (this.hostNameVerifierClassName == null - || hostNameVerifierClassName.equals("")) { + if (this.hostNameVerifierClassName == null || hostNameVerifierClassName.equals("")) { return null; } try { @@ -1212,6 +1127,7 @@ public class JNDIRealm extends RealmBase } } + /** * Set the {@link SSLSocketFactory} to be used when opening connections * using StartTLS. An instance of the factory with the given name will be @@ -1225,6 +1141,7 @@ public class JNDIRealm extends RealmBase this.sslSocketFactoryClassName = factoryClassName; } + /** * Set the ssl protocol to be used for connections using StartTLS. * @@ -1235,6 +1152,7 @@ public class JNDIRealm extends RealmBase this.sslProtocol = protocol; } + /** * @return the list of supported ssl protocols by the default * {@link SSLContext} @@ -1248,12 +1166,14 @@ public class JNDIRealm extends RealmBase } } + private Object constructInstance(String className) throws ReflectiveOperationException { Class<?> clazz = Class.forName(className); return clazz.getConstructor().newInstance(); } + /** * Sets whether to use the context or default ClassLoader. * True means use context ClassLoader. @@ -1264,6 +1184,7 @@ public class JNDIRealm extends RealmBase useContextClassLoader = useContext; } + /** * Returns whether to use the context or default ClassLoader. * True means to use the context ClassLoader. @@ -1274,6 +1195,7 @@ public class JNDIRealm extends RealmBase return useContextClassLoader; } + // ---------------------------------------------------------- Realm Methods /** @@ -1333,7 +1255,7 @@ public class JNDIRealm extends RealmBase closePooledConnections(); // open a new directory context. - connection = get(); + connection = get(true); // Try the authentication again. principal = authenticate(connection, username, credentials); @@ -1356,21 +1278,14 @@ public class JNDIRealm extends RealmBase closePooledConnections(); // Return "not authenticated" for this request - if (containerLog.isDebugEnabled()) + if (containerLog.isDebugEnabled()) { containerLog.debug("Returning null principal."); + } return null; - } - } - // -------------------------------------------------------- Package Methods - - - // ------------------------------------------------------ Protected Methods - - /** * Return the Principal associated with the specified username and * credentials, if there is one; otherwise return <code>null</code>. @@ -1383,22 +1298,18 @@ public class JNDIRealm extends RealmBase * * @exception NamingException if a directory server error occurs */ - public Principal authenticate(JNDIConnection connection, - String username, - String credentials) - throws NamingException { - - if (username == null || username.equals("") - || credentials == null || credentials.equals("")) { - if (containerLog.isDebugEnabled()) + public Principal authenticate(JNDIConnection connection, String username, String credentials) + throws NamingException { + + if (username == null || username.equals("") || credentials == null || credentials.equals("")) { + if (containerLog.isDebugEnabled()) { containerLog.debug("username null or empty: returning null principal."); + } return null; } if (userPatternArray != null) { - for (int curUserPattern = 0; - curUserPattern < userPatternArray.length; - curUserPattern++) { + for (int curUserPattern = 0; curUserPattern < userPatternArray.length; curUserPattern++) { // Retrieve user information User user = getUser(connection, username, credentials, curUserPattern); if (user != null) { @@ -1426,12 +1337,14 @@ public class JNDIRealm extends RealmBase } else { // Retrieve user information User user = getUser(connection, username, credentials); - if (user == null) + if (user == null) { return null; + } // Check the user's credentials - if (!checkCredentials(connection.context, user, credentials)) + if (!checkCredentials(connection.context, user, credentials)) { return null; + } // Search for additional roles List<String> roles = getRoles(connection, user); @@ -1445,6 +1358,8 @@ public class JNDIRealm extends RealmBase } + // ------------------------------------------------------ Protected Methods + /** * Return a User object containing information about the user * with the specified username, if found in the directory; @@ -1457,9 +1372,7 @@ public class JNDIRealm extends RealmBase * * @see #getUser(JNDIConnection, String, String, int) */ - protected User getUser(JNDIConnection connection, String username) - throws NamingException { - + protected User getUser(JNDIConnection connection, String username) throws NamingException { return getUser(connection, username, null, -1); } @@ -1477,9 +1390,7 @@ public class JNDIRealm extends RealmBase * * @see #getUser(JNDIConnection, String, String, int) */ - protected User getUser(JNDIConnection connection, String username, String credentials) - throws NamingException { - + protected User getUser(JNDIConnection connection, String username, String credentials) throws NamingException { return getUser(connection, username, credentials, -1); } @@ -1502,18 +1413,19 @@ public class JNDIRealm extends RealmBase * @return the User object * @exception NamingException if a directory server error occurs */ - protected User getUser(JNDIConnection connection, String username, - String credentials, int curUserPattern) - throws NamingException { + protected User getUser(JNDIConnection connection, String username, String credentials, int curUserPattern) + throws NamingException { User user = null; // Get attributes to retrieve from user entry List<String> list = new ArrayList<>(); - if (userPassword != null) + if (userPassword != null) { list.add(userPassword); - if (userRoleName != null) + } + if (userRoleName != null) { list.add(userRoleName); + } if (userRoleAttribute != null) { list.add(userRoleAttribute); } @@ -1545,8 +1457,7 @@ public class JNDIRealm extends RealmBase if (userPassword == null && credentials != null && user != null) { // The password is available. Insert it since it may be required for // role searches. - return new User(user.getUserName(), user.getDN(), credentials, - user.getRoles(), user.getUserRoleId()); + return new User(user.getUserName(), user.getDN(), credentials, user.getRoles(), user.getUserRoleId()); } return user; @@ -1566,11 +1477,8 @@ public class JNDIRealm extends RealmBase * @return the User object * @exception NamingException if a directory server error occurs */ - protected User getUserByPattern(DirContext context, - String username, - String[] attrIds, - String dn) - throws NamingException { + protected User getUserByPattern(DirContext context, String username, String[] attrIds, String dn) + throws NamingException { // If no attributes are requested, no need to look for them if (attrIds == null || attrIds.length == 0) { @@ -1584,13 +1492,15 @@ public class JNDIRealm extends RealmBase } catch (NameNotFoundException e) { return null; } - if (attrs == null) + if (attrs == null) { return null; + } // Retrieve value of userPassword String password = null; - if (userPassword != null) + if (userPassword != null) { password = getAttributeValue(userPassword, attrs); + } String userRoleAttrValue = null; if (userRoleAttribute != null) { @@ -1599,8 +1509,9 @@ public class JNDIRealm extends RealmBase // Retrieve values of userRoleName attribute ArrayList<String> roles = null; - if (userRoleName != null) + if (userRoleName != null) { roles = addAttributeValues(userRoleName, attrs, roles); + } return new User(username, dn, password, roles, userRoleAttrValue); } @@ -1621,20 +1532,20 @@ public class JNDIRealm extends RealmBase * @exception NamingException if a directory server error occurs * @see #getUserByPattern(DirContext, String, String[], String) */ - protected User getUserByPattern(JNDIConnection connection, - String username, - String credentials, - String[] attrIds, - int curUserPattern) - throws NamingException { + protected User getUserByPattern(JNDIConnection connection, String username, String credentials, String[] attrIds, + int curUserPattern) throws NamingException { User user = null; - if (username == null || userPatternArray[curUserPattern] == null) + if (username == null || userPatternArray[curUserPattern] == null) { return null; + } - // Form the dn from the user pattern - String dn = connection.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 = connection.userPatternFormatArray[curUserPattern].format( + new String[] { doAttributeValueEscaping(username) }); try { user = getUserByPattern(connection.context, username, attrIds, dn); @@ -1666,16 +1577,17 @@ public class JNDIRealm extends RealmBase * @return the User object * @exception NamingException if a directory server error occurs */ - protected User getUserBySearch(JNDIConnection connection, - String username, - String[] attrIds) - throws NamingException { + protected User getUserBySearch(JNDIConnection connection, String username, String[] attrIds) + throws NamingException { - if (username == null || connection.userSearchFormat == null) + if (username == null || connection.userSearchFormat == null) { return null; + } // Form the search filter - String filter = connection.userSearchFormat.format(new String[] { username }); + // Escape in case username contains a character with special meaning in + // a search filter. + String filter = connection.userSearchFormat.format(new String[] { doFilterEscaping(username) }); // Set up the search controls SearchControls constraints = new SearchControls(); @@ -1690,12 +1602,12 @@ public class JNDIRealm extends RealmBase constraints.setTimeLimit(timeLimit); // Specify the attributes to be retrieved - if (attrIds == null) + if (attrIds == null) { attrIds = new String[0]; + } constraints.setReturningAttributes(attrIds); - NamingEnumeration<SearchResult> results = - connection.context.search(userBase, filter, constraints); + NamingEnumeration<SearchResult> results = connection.context.search(userBase, filter, constraints); try { // Fail if no entries found @@ -1704,10 +1616,11 @@ public class JNDIRealm extends RealmBase return null; } } catch (PartialResultException ex) { - if (!adCompat) + if (!adCompat) { throw ex; - else + } else { return null; + } } // Get result for the first entry found @@ -1722,24 +1635,28 @@ public class JNDIRealm extends RealmBase return null; } } catch (PartialResultException ex) { - if (!adCompat) + if (!adCompat) { throw ex; + } } String dn = getDistinguishedName(connection.context, userBase, result); - if (containerLog.isTraceEnabled()) + if (containerLog.isTraceEnabled()) { containerLog.trace(" entry found for " + username + " with dn " + dn); + } // Get the entry's attributes Attributes attrs = result.getAttributes(); - if (attrs == null) + if (attrs == null) { return null; + } // Retrieve value of userPassword String password = null; - if (userPassword != null) + if (userPassword != null) { password = getAttributeValue(userPassword, attrs); + } String userRoleAttrValue = null; if (userRoleAttribute != null) { @@ -1748,8 +1665,9 @@ public class JNDIRealm extends RealmBase // Retrieve values of userRoleName attribute ArrayList<String> roles = null; - if (userRoleName != null) + if (userRoleName != null) { roles = addAttributeValues(userRoleName, attrs, roles); + } return new User(username, dn, password, roles, userRoleAttrValue); } finally { @@ -1775,30 +1693,25 @@ public class JNDIRealm extends RealmBase * @return <code>true</code> if the credentials are validated * @exception NamingException if a directory server error occurs */ - protected boolean checkCredentials(DirContext context, - User user, - String credentials) - throws NamingException { - - boolean validated = false; - - if (userPassword == null) { - validated = bindAsUser(context, user, credentials); - } else { - validated = compareCredentials(context, user, credentials); - } - - if (containerLog.isTraceEnabled()) { - if (validated) { - containerLog.trace(sm.getString("jndiRealm.authenticateSuccess", - user.getUserName())); - } else { - containerLog.trace(sm.getString("jndiRealm.authenticateFailure", - user.getUserName())); - } - } - return validated; - } + protected boolean checkCredentials(DirContext context, User user, String credentials) throws NamingException { + + boolean validated = false; + + if (userPassword == null) { + validated = bindAsUser(context, user, credentials); + } else { + validated = compareCredentials(context, user, credentials); + } + + if (containerLog.isTraceEnabled()) { + if (validated) { + containerLog.trace(sm.getString("jndiRealm.authenticateSuccess", user.getUserName())); + } else { + containerLog.trace(sm.getString("jndiRealm.authenticateFailure", user.getUserName())); + } + } + return validated; + } /** @@ -1811,17 +1724,15 @@ public class JNDIRealm extends RealmBase * @return <code>true</code> if the credentials are validated * @exception NamingException if a directory server error occurs */ - protected boolean compareCredentials(DirContext context, - User info, - String credentials) - throws NamingException { - + protected boolean compareCredentials(DirContext context, User info, String credentials) throws NamingException { // Validate the credentials specified by the user - if (containerLog.isTraceEnabled()) + if (containerLog.isTraceEnabled()) { containerLog.trace(" validating credentials"); + } - if (info == null || credentials == null) + if (info == null || credentials == null) { return false; + } String password = info.getPassword(); @@ -1838,21 +1749,22 @@ public class JNDIRealm extends RealmBase * @return <code>true</code> if the credentials are validated * @exception NamingException if a directory server error occurs */ - protected boolean bindAsUser(DirContext context, - User user, - String credentials) - throws NamingException { - - if (credentials == null || user == null) - return false; - - String dn = user.getDN(); - if (dn == null) - return false; - - // Validate the credentials specified by the user - if (containerLog.isTraceEnabled()) { - containerLog.trace(" validating credentials by binding as the user"); + protected boolean bindAsUser(DirContext context, User user, String credentials) throws NamingException { + + if (credentials == null || user == null) { + return false; + } + + // This is returned from the directory so will be attribute value + // escaped if required + String dn = user.getDN(); + if (dn == null) { + return false; + } + + // Validate the credentials specified by the user + if (containerLog.isTraceEnabled()) { + containerLog.trace(" validating credentials by binding as the user"); } userCredentialsAdd(context, dn, credentials); @@ -1877,48 +1789,47 @@ public class JNDIRealm extends RealmBase return validated; } - /** - * Configure the context to use the provided credentials for - * authentication. - * - * @param context DirContext to configure - * @param dn Distinguished name of user - * @param credentials Credentials of user - * @exception NamingException if a directory server error occurs - */ - private void userCredentialsAdd(DirContext context, String dn, - String credentials) throws NamingException { + + /** + * Configure the context to use the provided credentials for + * authentication. + * + * @param context DirContext to configure + * @param dn Distinguished name of user + * @param credentials Credentials of user + * @exception NamingException if a directory server error occurs + */ + private void userCredentialsAdd(DirContext context, String dn, String credentials) throws NamingException { // Set up security environment to bind as the user context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn); context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials); } + /** * Configure the context to use {@link #connectionName} and * {@link #connectionPassword} if specified or an anonymous connection if * those attributes are not specified. * - * @param context DirContext to configure - * @exception NamingException if a directory server error occurs + * @param context DirContext to configure + * @exception NamingException if a directory server error occurs */ - private void userCredentialsRemove(DirContext context) - throws NamingException { + private void userCredentialsRemove(DirContext context) throws NamingException { // Restore the original security environment if (connectionName != null) { - context.addToEnvironment(Context.SECURITY_PRINCIPAL, - connectionName); + context.addToEnvironment(Context.SECURITY_PRINCIPAL, connectionName); } else { context.removeFromEnvironment(Context.SECURITY_PRINCIPAL); } if (connectionPassword != null) { - context.addToEnvironment(Context.SECURITY_CREDENTIALS, - connectionPassword); + context.addToEnvironment(Context.SECURITY_CREDENTIALS, connectionPassword); } else { context.removeFromEnvironment(Context.SECURITY_CREDENTIALS); } } + /** * Return a List of roles associated with the given User. Any * roles present in the user's directory entry are supplemented by @@ -1930,21 +1841,27 @@ public class JNDIRealm extends RealmBase * @return the list of role names * @exception NamingException if a directory server error occurs */ - protected List<String> getRoles(JNDIConnection connection, User user) - throws NamingException { + protected List<String> getRoles(JNDIConnection connection, User user) throws NamingException { - if (user == null) + if (user == null) { return null; + } + // This is returned from the directory so will be attribute value + // escaped if required String dn = user.getDN(); + // This is the name the user provided to the authentication process so + // it will not be escaped String username = user.getUserName(); String userRoleId = user.getUserRoleId(); - if (dn == null || username == null) + if (dn == null || username == null) { return null; + } - if (containerLog.isTraceEnabled()) + if (containerLog.isTraceEnabled()) { containerLog.trace(" getRoles(" + dn + ")"); + } // Start with roles retrieved from the user entry List<String> list = new ArrayList<>(); @@ -1952,8 +1869,9 @@ public class JNDIRealm extends RealmBase if (userRoles != null) { list.addAll(userRoles); } - if (commonRole != null) + if (commonRole != null) { list.add(commonRole); + } if (containerLog.isTraceEnabled()) { containerLog.trace(" Found " + list.size() + " user internal roles"); @@ -1961,16 +1879,23 @@ public class JNDIRealm extends RealmBase } // Are we configured to do role searches? - if ((connection.roleFormat == null) || (roleName == null)) + if ((connection.roleFormat == null) || (roleName == null)) { return list; + } - // Set up parameters for an appropriate search - String filter = connection.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 = connection.roleFormat.format(new String[] { + doFilterEscaping(dn), + doFilterEscaping(doAttributeValueEscaping(username)), + doFilterEscaping(doAttributeValueEscaping(userRoleId)) }); SearchControls controls = new SearchControls(); - if (roleSubtree) + if (roleSubtree) { controls.setSearchScope(SearchControls.SUBTREE_SCOPE); - else + } else { controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); + } controls.setReturningAttributes(new String[] {roleName}); String base = null; @@ -1979,7 +1904,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 = connection.roleBaseFormat.format(nameParts); } else { @@ -1990,25 +1917,28 @@ public class JNDIRealm extends RealmBase NamingEnumeration<SearchResult> results = searchAsUser(connection.context, user, base, filter, controls, isRoleSearchAsUser()); - if (results == null) + if (results == null) { return list; // Should never happen, but just in case ... + } Map<String, String> groupMap = new HashMap<>(); try { while (results.hasMore()) { SearchResult result = results.next(); Attributes attrs = result.getAttributes(); - if (attrs == null) + if (attrs == null) { continue; - String dname = getDistinguishedName(connection.context, roleBase, result); + } + String dname = getDistinguishedName(connection.context, base, result); String name = getAttributeValue(roleName, attrs); if (name != null && dname != null) { groupMap.put(dname, name); } } } catch (PartialResultException ex) { - if (!adCompat) + if (!adCompat) { throw ex; + } } finally { results.close(); } @@ -2033,22 +1963,28 @@ 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 = connection.roleFormat.format(new String[] { doRFC2254Encoding(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 = connection.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(connection.context, user, roleBase, filter, controls, - isRoleSearchAsUser()); + results = searchAsUser(connection.context, user, base, filter, controls, isRoleSearchAsUser()); try { while (results.hasMore()) { SearchResult result = results.next(); Attributes attrs = result.getAttributes(); - if (attrs == null) + if (attrs == null) { continue; + } String dname = getDistinguishedName(connection.context, roleBase, result); String name = getAttributeValue(roleName, attrs); if (name != null && dname != null && !groupMap.keySet().contains(dname)) { @@ -2058,12 +1994,12 @@ public class JNDIRealm extends RealmBase if (containerLog.isTraceEnabled()) { containerLog.trace(" Found nested role " + dname + " -> " + name); } - } - } + } } catch (PartialResultException ex) { - if (!adCompat) + if (!adCompat) { throw ex; + } } finally { results.close(); } @@ -2077,6 +2013,7 @@ public class JNDIRealm extends RealmBase return list; } + /** * Perform the search on the context as the {@code dn}, when * {@code searchAsUser} is {@code true}, otherwise search the context with @@ -2099,8 +2036,7 @@ public class JNDIRealm extends RealmBase * @throws NamingException * if a directory server error occurs */ - private NamingEnumeration<SearchResult> searchAsUser(DirContext context, - User user, String base, String filter, + private NamingEnumeration<SearchResult> searchAsUser(DirContext context, User user, String base, String filter, SearchControls controls, boolean searchAsUser) throws NamingException { NamingEnumeration<SearchResult> results; try { @@ -2125,26 +2061,30 @@ public class JNDIRealm extends RealmBase * @return the attribute value * @exception NamingException if a directory server error occurs */ - private String getAttributeValue(String attrId, Attributes attrs) - throws NamingException { + private String getAttributeValue(String attrId, Attributes attrs) throws NamingException { - if (containerLog.isTraceEnabled()) + if (containerLog.isTraceEnabled()) { containerLog.trace(" retrieving attribute " + attrId); + } - if (attrId == null || attrs == null) + if (attrId == null || attrs == null) { return null; + } Attribute attr = attrs.get(attrId); - if (attr == null) + if (attr == null) { return null; + } Object value = attr.get(); - if (value == null) + if (value == null) { return null; + } String valueString = null; - if (value instanceof byte[]) + if (value instanceof byte[]) { valueString = new String((byte[]) value); - else + } else { valueString = value.toString(); + } return valueString; } @@ -2159,20 +2099,22 @@ public class JNDIRealm extends RealmBase * @return the list of attribute values * @exception NamingException if a directory server error occurs */ - private ArrayList<String> addAttributeValues(String attrId, - Attributes attrs, - ArrayList<String> values) - throws NamingException{ + private ArrayList<String> addAttributeValues(String attrId, Attributes attrs, ArrayList<String> values) + throws NamingException { - if (containerLog.isTraceEnabled()) + if (containerLog.isTraceEnabled()) { containerLog.trace(" retrieving values for attribute " + attrId); - if (attrId == null || attrs == null) + } + if (attrId == null || attrs == null) { return values; - if (values == null) + } + if (values == null) { values = new ArrayList<>(); + } Attribute attr = attrs.get(attrId); - if (attr == null) + if (attr == null) { return values; + } NamingEnumeration<?> e = attr.getAll(); try { while(e.hasMore()) { @@ -2180,8 +2122,9 @@ public class JNDIRealm extends RealmBase values.add(value); } } catch (PartialResultException ex) { - if (!adCompat) + if (!adCompat) { throw ex; + } } finally { e.close(); } @@ -2214,8 +2157,9 @@ public class JNDIRealm extends RealmBase } // Close our opened connection try { - if (containerLog.isDebugEnabled()) + if (containerLog.isDebugEnabled()) { containerLog.debug("Closing directory context"); + } connection.context.close(); } catch (NamingException e) { containerLog.error(sm.getString("jndiRealm.close"), e); @@ -2225,9 +2169,9 @@ public class JNDIRealm extends RealmBase if (connectionPool == null) { singleConnectionLock.unlock(); } - } + /** * Close all pooled connections. */ @@ -2243,6 +2187,7 @@ public class JNDIRealm extends RealmBase } } + /** * Get the password for the specified user. * @param username The user name @@ -2258,7 +2203,6 @@ public class JNDIRealm extends RealmBase JNDIConnection connection = null; User user = null; try { - // Ensure that we have a directory context available connection = get(); @@ -2281,7 +2225,6 @@ public class JNDIRealm extends RealmBase user = getUser(connection, username, null); } - // Release this context release(connection); @@ -2292,15 +2235,14 @@ public class JNDIRealm extends RealmBase // ... and have a password return user.getPassword(); } - } catch (NamingException e) { // Log the problem for posterity containerLog.error(sm.getString("jndiRealm.exception"), e); return null; } - } + /** * Get the principal associated with the specified certificate. * @param username The user name @@ -2311,9 +2253,9 @@ public class JNDIRealm extends RealmBase return getPrincipal(username, null); } + @Override - protected Principal getPrincipal(GSSName gssName, - GSSCredential gssCredential) { + protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential) { String name = gssName.toString(); if (isStripRealmForGss()) { @@ -2327,15 +2269,14 @@ public class JNDIRealm extends RealmBase return getPrincipal(name, gssCredential); } + @Override - protected Principal getPrincipal(String username, - GSSCredential gssCredential) { + protected Principal getPrincipal(String username, GSSCredential gssCredential) { JNDIConnection connection = null; Principal principal = null; try { - // Ensure that we have a directory context available connection = get(); @@ -2347,7 +2288,6 @@ public class JNDIRealm extends RealmBase principal = getPrincipal(connection, username, gssCredential); } catch (CommunicationException | ServiceUnavailableException e) { - // log the exception so we know it's there. containerLog.info(sm.getString("jndiRealm.exception.retry"), e); @@ -2360,10 +2300,8 @@ public class JNDIRealm extends RealmBase // Try the authentication again. principal = getPrincipal(connection, username, gssCredential); - } - // Release this context release(connection); @@ -2371,16 +2309,12 @@ public class JNDIRealm extends RealmBase return principal; } catch (NamingException e) { - // Log the problem for posterity containerLog.error(sm.getString("jndiRealm.exception"), e); // Return "not authenticated" for this request return null; - } - - } @@ -2392,9 +2326,8 @@ public class JNDIRealm extends RealmBase * @return the Principal associated with the given certificate. * @exception NamingException if a directory server error occurs */ - protected Principal getPrincipal(JNDIConnection connection, - String username, GSSCredential gssCredential) - throws NamingException { + protected Principal getPrincipal(JNDIConnection connection, String username, GSSCredential gssCredential) + throws NamingException { User user = null; List<String> roles = null; @@ -2406,12 +2339,9 @@ public class JNDIRealm extends RealmBase // Preserve the current context environment parameters preservedEnvironment = context.getEnvironment(); // Set up context - context.addToEnvironment( - Context.SECURITY_AUTHENTICATION, "GSSAPI"); - context.addToEnvironment( - "javax.security.sasl.server.authentication", "true"); - context.addToEnvironment( - "javax.security.sasl.qop", spnegoDelegationQop); + context.addToEnvironment(Context.SECURITY_AUTHENTICATION, "GSSAPI"); + context.addToEnvironment("javax.security.sasl.server.authentication", "true"); + context.addToEnvironment("javax.security.sasl.qop", spnegoDelegationQop); // Note: Subject already set in SPNEGO authenticator so no need // for Subject.doAs() here } @@ -2421,23 +2351,20 @@ public class JNDIRealm extends RealmBase } } finally { if (gssCredential != null && isUseDelegatedCredential()) { - restoreEnvironmentParameter(context, - Context.SECURITY_AUTHENTICATION, preservedEnvironment); - restoreEnvironmentParameter(context, - "javax.security.sasl.server.authentication", preservedEnvironment); - restoreEnvironmentParameter(context, "javax.security.sasl.qop", - preservedEnvironment); + restoreEnvironmentParameter(context, Context.SECURITY_AUTHENTICATION, preservedEnvironment); + restoreEnvironmentParameter(context, "javax.security.sasl.server.authentication", preservedEnvironment); + restoreEnvironmentParameter(context, "javax.security.sasl.qop", preservedEnvironment); } } if (user != null) { - return new GenericPrincipal(user.getUserName(), user.getPassword(), - roles, null, null, gssCredential); + return new GenericPrincipal(user.getUserName(), user.getPassword(), roles, null, null, gssCredential); } return null; } + private void restoreEnvironmentParameter(DirContext context, String parameterName, Hashtable<?, ?> preservedEnvironment) { try { @@ -2451,6 +2378,7 @@ public class JNDIRealm extends RealmBase } } + /** * Open (if necessary) and return a connection to the configured * directory server for this Realm. @@ -2458,12 +2386,28 @@ public class JNDIRealm extends RealmBase * @exception NamingException if a directory server error occurs */ protected JNDIConnection get() throws NamingException { + return get(false); + } + + /** + * Open (if necessary) and return a connection to the configured + * directory server for this Realm. + * @param create when pooling, this forces creation of a new connection, + * for example after an error + * @return the connection + * @exception NamingException if a directory server error occurs + */ + protected JNDIConnection get(boolean create) throws NamingException { JNDIConnection connection = null; // Use the pool if available, otherwise use the single connection if (connectionPool != null) { - connection = connectionPool.pop(); - if (connection == null) { + if (create) { connection = create(); + } else { + connection = connectionPool.pop(); + if (connection == null) { + connection = create(); + } } } else { singleConnectionLock.lock(); @@ -2475,6 +2419,7 @@ public class JNDIRealm extends RealmBase return connection; } + /** * Release our use of this connection so that it can be recycled. * @@ -2491,6 +2436,7 @@ public class JNDIRealm extends RealmBase } } + /** * Create a new connection wrapper, along with the * message formats. @@ -2505,8 +2451,7 @@ public class JNDIRealm extends RealmBase int len = userPatternArray.length; connection.userPatternFormatArray = new MessageFormat[len]; for (int i = 0; i < len; i++) { - connection.userPatternFormatArray[i] = - new MessageFormat(userPatternArray[i]); + connection.userPatternFormatArray[i] = new MessageFormat(userPatternArray[i]); } } if (roleBase != null) { @@ -2518,6 +2463,7 @@ public class JNDIRealm extends RealmBase return connection; } + /** * Create a new connection to the directory server. * @param connection The directory server connection wrapper @@ -2552,12 +2498,14 @@ public class JNDIRealm extends RealmBase } } + @Override public boolean isAvailable() { // Simple best effort check return (connectionPool != null || singleConnection.context != null); } + private DirContext createDirContext(Hashtable<String, String> env) throws NamingException { if (useStartTls) { return createTlsDirContext(env); @@ -2566,13 +2514,13 @@ public class JNDIRealm extends RealmBase } } + private SSLSocketFactory getSSLSocketFactory() { if (sslSocketFactory != null) { return sslSocketFactory; } final SSLSocketFactory result; - if (this.sslSocketFactoryClassName != null - && !sslSocketFactoryClassName.trim().equals("")) { + if (this.sslSocketFactoryClassName != null && !sslSocketFactoryClassName.trim().equals("")) { result = createSSLSocketFactoryFromClassName(this.sslSocketFactoryClassName); } else { result = createSSLContextFactoryFromProtocol(sslProtocol); @@ -2581,6 +2529,7 @@ public class JNDIRealm extends RealmBase return result; } + private SSLSocketFactory createSSLSocketFactoryFromClassName(String className) { try { Object o = constructInstance(className); @@ -2598,6 +2547,7 @@ public class JNDIRealm extends RealmBase } } + private SSLSocketFactory createSSLContextFactoryFromProtocol(String protocol) { try { SSLContext sslContext; @@ -2609,14 +2559,13 @@ public class JNDIRealm extends RealmBase } return sslContext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { - List<String> allowedProtocols = Arrays - .asList(getSupportedSslProtocols()); - throw new IllegalArgumentException( - sm.getString("jndiRealm.invalidSslProtocol", protocol, - allowedProtocols), e); + List<String> allowedProtocols = Arrays.asList(getSupportedSslProtocols()); + throw new IllegalArgumentException(sm.getString("jndiRealm.invalidSslProtocol", + protocol, allowedProtocols), e); } } + /** * Create a tls enabled LdapContext and set the StartTlsResponse tls * instance variable. @@ -2627,12 +2576,10 @@ public class JNDIRealm extends RealmBase * @throws NamingException * when something goes wrong while negotiating the connection */ - private DirContext createTlsDirContext( - Hashtable<String, String> env) throws NamingException { + private DirContext createTlsDirContext(Hashtable<String, String> env) throws NamingException { Map<String, Object> savedEnv = new HashMap<>(); - for (String key : Arrays.asList(Context.SECURITY_AUTHENTICATION, - Context.SECURITY_CREDENTIALS, Context.SECURITY_PRINCIPAL, - Context.SECURITY_PROTOCOL)) { + for (String key : Arrays.asList(Context.SECURITY_AUTHENTICATION, Context.SECURITY_CREDENTIALS, + Context.SECURITY_PRINCIPAL, Context.SECURITY_PROTOCOL)) { Object entry = env.remove(key); if (entry != null) { savedEnv.put(key, entry); @@ -2641,8 +2588,7 @@ public class JNDIRealm extends RealmBase LdapContext result = null; try { result = new InitialLdapContext(env, null); - tls = (StartTlsResponse) result - .extendedOperation(new StartTlsRequest()); + tls = (StartTlsResponse) result.extendedOperation(new StartTlsRequest()); if (getHostnameVerifier() != null) { tls.setHostnameVerifier(getHostnameVerifier()); } @@ -2651,22 +2597,21 @@ public class JNDIRealm extends RealmBase } try { SSLSession negotiate = tls.negotiate(getSSLSocketFactory()); - containerLog.debug(sm.getString("jndiRealm.negotiatedTls", - negotiate.getProtocol())); + containerLog.debug(sm.getString("jndiRealm.negotiatedTls", negotiate.getProtocol())); } catch (IOException e) { throw new NamingException(e.getMessage()); } } finally { if (result != null) { for (Map.Entry<String, Object> savedEntry : savedEnv.entrySet()) { - result.addToEnvironment(savedEntry.getKey(), - savedEntry.getValue()); + result.addToEnvironment(savedEntry.getKey(), savedEntry.getValue()); } } } return result; } + /** * Create our directory context configuration. * @@ -2677,40 +2622,48 @@ public class JNDIRealm extends RealmBase Hashtable<String,String> env = new Hashtable<>(); // Configure our directory context environment. - if (containerLog.isDebugEnabled() && connectionAttempt == 0) + if (containerLog.isDebugEnabled() && connectionAttempt == 0) { containerLog.debug("Connecting to URL " + connectionURL); - else if (containerLog.isDebugEnabled() && connectionAttempt > 0) + } else if (containerLog.isDebugEnabled() && connectionAttempt > 0) { containerLog.debug("Connecting to URL " + alternateURL); + } env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); - if (connectionName != null) + if (connectionName != null) { env.put(Context.SECURITY_PRINCIPAL, connectionName); - if (connectionPassword != null) + } + if (connectionPassword != null) { env.put(Context.SECURITY_CREDENTIALS, connectionPassword); - if (connectionURL != null && connectionAttempt == 0) + } + if (connectionURL != null && connectionAttempt == 0) { env.put(Context.PROVIDER_URL, connectionURL); - else if (alternateURL != null && connectionAttempt > 0) + } else if (alternateURL != null && connectionAttempt > 0) { env.put(Context.PROVIDER_URL, alternateURL); - if (authentication != null) + } + if (authentication != null) { env.put(Context.SECURITY_AUTHENTICATION, authentication); - if (protocol != null) + } + if (protocol != null) { env.put(Context.SECURITY_PROTOCOL, protocol); - if (referrals != null) + } + if (referrals != null) { env.put(Context.REFERRAL, referrals); - if (derefAliases != null) + } + if (derefAliases != null) { env.put(JNDIRealm.DEREF_ALIASES, derefAliases); - if (connectionTimeout != null) + } + if (connectionTimeout != null) { env.put("com.sun.jndi.ldap.connect.timeout", connectionTimeout); - if (readTimeout != null) + } + if (readTimeout != null) { env.put("com.sun.jndi.ldap.read.timeout", readTimeout); + } return env; - } // ------------------------------------------------------ Lifecycle Methods - /** * Prepare for the beginning of active use of the public methods of this * component and implement the requirements of @@ -2752,7 +2705,7 @@ public class JNDIRealm extends RealmBase * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ - @Override + @Override protected void stopInternal() throws LifecycleException { super.stopInternal(); // Close any open directory server connection @@ -2765,6 +2718,7 @@ public class JNDIRealm extends RealmBase } } + /** * Given a string containing LDAP patterns for user locations (separated by * parentheses in a pseudo-LDAP search string format - @@ -2799,8 +2753,7 @@ public class JNDIRealm extends RealmBase while (userPatternString.charAt(endParenLoc - 1) == '\\') { endParenLoc = userPatternString.indexOf(')', endParenLoc+1); } - String nextPathPart = userPatternString.substring - (startParenLoc+1, endParenLoc); + String nextPathPart = userPatternString.substring(startParenLoc+1, endParenLoc); pathList.add(nextPathPart); startingPoint = endParenLoc+1; startParenLoc = userPatternString.indexOf('(', startingPoint); @@ -2808,7 +2761,6 @@ public class JNDIRealm extends RealmBase return pathList.toArray(new String[] {}); } return null; - } @@ -2823,10 +2775,36 @@ public class JNDIRealm extends RealmBase * ) -> \29 * \ -> \5c * \0 -> \00 + * * @param inString string to escape according to RFC 2254 guidelines + * * @return String the escaped/encoded result + * + * @deprecated Will be removed in Tomcat 10.1.x onwards */ + @Deprecated 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); @@ -2864,47 +2842,42 @@ public class JNDIRealm extends RealmBase * @return String containing the distinguished name * @exception NamingException if a directory server error occurs */ - protected String getDistinguishedName(DirContext context, String base, - SearchResult result) throws NamingException { + protected String getDistinguishedName(DirContext context, String base, SearchResult result) throws NamingException { // Get the entry's distinguished name. For relative results, this means // we need to composite a name with the base name, the context name, and // the result name. For non-relative names, use the returned name. String resultName = result.getName(); Name name; if (result.isRelative()) { - if (containerLog.isTraceEnabled()) { - containerLog.trace(" search returned relative name: " + resultName); - } - NameParser parser = context.getNameParser(""); - Name contextName = parser.parse(context.getNameInNamespace()); - Name baseName = parser.parse(base); + if (containerLog.isTraceEnabled()) { + containerLog.trace(" search returned relative name: " + resultName); + } + NameParser parser = context.getNameParser(""); + Name contextName = parser.parse(context.getNameInNamespace()); + Name baseName = parser.parse(base); - // Bugzilla 32269 - Name entryName = parser.parse(new CompositeName(resultName).get(0)); + // Bugzilla 32269 + Name entryName = parser.parse(new CompositeName(resultName).get(0)); - name = contextName.addAll(baseName); - name = name.addAll(entryName); + name = contextName.addAll(baseName); + name = name.addAll(entryName); } else { - if (containerLog.isTraceEnabled()) { - containerLog.trace(" search returned absolute name: " + resultName); - } - try { - // Normalize the name by running it through the name parser. - NameParser parser = context.getNameParser(""); - URI userNameUri = new URI(resultName); - String pathComponent = userNameUri.getPath(); - // Should not ever have an empty path component, since that is /{DN} - if (pathComponent.length() < 1 ) { - throw new InvalidNameException( - "Search returned unparseable absolute name: " + - resultName ); - } - name = parser.parse(pathComponent.substring(1)); - } catch ( URISyntaxException e ) { - throw new InvalidNameException( - "Search returned unparseable absolute name: " + - resultName ); - } + if (containerLog.isTraceEnabled()) { + containerLog.trace(" search returned absolute name: " + resultName); + } + try { + // Normalize the name by running it through the name parser. + NameParser parser = context.getNameParser(""); + URI userNameUri = new URI(resultName); + String pathComponent = userNameUri.getPath(); + // Should not ever have an empty path component, since that is /{DN} + if (pathComponent.length() < 1 ) { + throw new InvalidNameException("Search returned unparseable absolute name: " + resultName); + } + name = parser.parse(pathComponent.substring(1)); + } catch ( URISyntaxException e ) { + throw new InvalidNameException("Search returned unparseable absolute name: " + resultName); + } } if (getForceDnHexEscape()) { @@ -2916,6 +2889,78 @@ 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. @@ -2992,7 +3037,7 @@ public class JNDIRealm extends RealmBase } - // ------------------------------------------------------ Private Classes + // ------------------------------------------------------ Protected Classes /** * A protected class representing a User @@ -3005,9 +3050,7 @@ public class JNDIRealm extends RealmBase private final List<String> roles; private final String userRoleId; - - public User(String username, String dn, String password, - List<String> roles, String userRoleId) { + public User(String username, String dn, String password, List<String> roles, String userRoleId) { this.username = username; this.dn = dn; this.password = password; @@ -3040,6 +3083,7 @@ public class JNDIRealm extends RealmBase } } + /** * Class holding the connection to the directory plus the associated * non thread safe message formats. @@ -3074,8 +3118,5 @@ public class JNDIRealm extends RealmBase * The directory context linking us to our directory server. */ protected DirContext context = null; - } - } - Index: apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java =================================================================== --- /dev/null +++ apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java @@ -0,0 +1,88 @@ + +@@ -0,0 +1,86 @@ +/* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +@RunWith(Parameterized.class) +public class TestJNDIRealmAttributeValueEscape { + + @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]") + public static Collection<Object[]> parameters() { + List<Object[]> parameterSets = new ArrayList<>(); + + // No escaping required + parameterSets.add(new String[] { "none", "none" }); + // Simple cases (same order as RFC 4512 section 2) + // Each appearing at the beginning, middle and ent + parameterSets.add(new String[] { " test", "\\20test" }); + parameterSets.add(new String[] { "te st", "te st" }); + parameterSets.add(new String[] { "test ", "test\\20" }); + parameterSets.add(new String[] { "#test", "\\23test" }); + parameterSets.add(new String[] { "te#st", "te#st" }); + parameterSets.add(new String[] { "test#", "test#" }); + parameterSets.add(new String[] { "\"test", "\\22test" }); + parameterSets.add(new String[] { "te\"st", "te\\22st" }); + parameterSets.add(new String[] { "test\"", "test\\22" }); + parameterSets.add(new String[] { "+test", "\\2Btest" }); + parameterSets.add(new String[] { "te+st", "te\\2Bst" }); + parameterSets.add(new String[] { "test+", "test\\2B" }); + parameterSets.add(new String[] { ",test", "\\2Ctest" }); + parameterSets.add(new String[] { "te,st", "te\\2Cst" }); + parameterSets.add(new String[] { "test,", "test\\2C" }); + parameterSets.add(new String[] { ";test", "\\3Btest" }); + parameterSets.add(new String[] { "te;st", "te\\3Bst" }); + parameterSets.add(new String[] { "test;", "test\\3B" }); + parameterSets.add(new String[] { "<test", "\\3Ctest" }); + parameterSets.add(new String[] { "te<st", "te\\3Cst" }); + parameterSets.add(new String[] { "test<", "test\\3C" }); + parameterSets.add(new String[] { ">test", "\\3Etest" }); + parameterSets.add(new String[] { "te>st", "te\\3Est" }); + parameterSets.add(new String[] { "test>", "test\\3E" }); + parameterSets.add(new String[] { "\\test", "\\5Ctest" }); + parameterSets.add(new String[] { "te\\st", "te\\5Cst" }); + parameterSets.add(new String[] { "test\\", "test\\5C" }); + parameterSets.add(new String[] { "\u0000test", "\\00test" }); + parameterSets.add(new String[] { "te\u0000st", "te\\00st" }); + parameterSets.add(new String[] { "test\u0000", "test\\00" }); + return parameterSets; + } + + + @Parameter(0) + public String in; + @Parameter(1) + public String out; + + private JNDIRealm realm = new JNDIRealm(); + + @Test + public void testConvertToHexEscape() throws Exception { + String result = realm.doAttributeValueEscaping(in); + Assert.assertEquals(out, result); + } +} Index: apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java =================================================================== --- /dev/null +++ apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java @@ -0,0 +1,263 @@ +/* + * 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.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +import org.apache.juli.logging.LogFactory; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.sdk.AddRequest; +import com.unboundid.ldap.sdk.LDAPConnection; +import com.unboundid.ldap.sdk.LDAPResult; +import com.unboundid.ldap.sdk.ResultCode; + +@RunWith(Parameterized.class) +public class TestJNDIRealmIntegration { + + private static final String USER_PATTERN = "cn={0},ou=people,dc=example,dc=com"; + private static final String USER_SEARCH = "cn={0}"; + private static final String USER_BASE = "ou=people,dc=example,dc=com"; + private static final String ROLE_SEARCH_A = "member={0}"; + private static final String ROLE_SEARCH_B = "member=cn={1},ou=people,dc=example,dc=com"; + private static final String ROLE_SEARCH_C = "member=cn={2},ou=people,dc=example,dc=com"; + private static final String ROLE_BASE = "ou=people,dc=example,dc=com"; + + private static InMemoryDirectoryServer ldapServer; + + @Parameterized.Parameters(name = "{index}: user[{5}], pwd[{6}]") + public static Collection<Object[]> parameters() { + List<Object[]> parameterSets = new ArrayList<>(); + for (String roleSearch : new String[] { ROLE_SEARCH_A, ROLE_SEARCH_B, ROLE_SEARCH_C }) { + addUsers(USER_PATTERN, null, null, roleSearch, ROLE_BASE, parameterSets); + addUsers(null, USER_SEARCH, USER_BASE, roleSearch, ROLE_BASE, parameterSets); + } + parameterSets.add(new Object[] { "cn={0},ou=s\\;ub,ou=people,dc=example,dc=com", null, null, ROLE_SEARCH_A, + "{3},ou=people,dc=example,dc=com", "testsub", "test", new String[] {"TestGroup4"} }); + return parameterSets; + } + + + private static void addUsers(String userPattern, String userSearch, String userBase, String roleSearch, + String roleBase, List<Object[]> parameterSets) { + parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase, + "test", "test", new String[] {"TestGroup"} }); + parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase, + "t;", "test", new String[] {"TestGroup"} }); + parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase, + "t*", "test", new String[] {"TestGroup"} }); + parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase, + "t=", "test", new String[] {"Test<Group*2", "Test>Group*3"} }); + } + + + @Parameter(0) + public String realmConfigUserPattern; + @Parameter(1) + public String realmConfigUserSearch; + @Parameter(2) + public String realmConfigUserBase; + @Parameter(3) + public String realmConfigRoleSearch; + @Parameter(4) + public String realmConfigRoleBase; + @Parameter(5) + public String username; + @Parameter(6) + public String credentials; + @Parameter(7) + public String[] groups; + + @Test + public void testAuthenication() throws Exception { + JNDIRealm realm = new JNDIRealm(); + realm.containerLog = LogFactory.getLog(TestJNDIRealmIntegration.class); + + realm.setConnectionURL("ldap://localhost:" + ldapServer.getListenPort()); + realm.setUserPattern(realmConfigUserPattern); + realm.setUserSearch(realmConfigUserSearch); + realm.setUserBase(realmConfigUserBase); + realm.setUserRoleAttribute("cn"); + realm.setRoleName("cn"); + realm.setRoleBase(realmConfigRoleBase); + realm.setRoleSearch(realmConfigRoleSearch); + realm.setRoleNested(true); + + GenericPrincipal p = (GenericPrincipal) realm.authenticate(username, credentials); + + Assert.assertNotNull(p); + Assert.assertEquals(username, p.name); + + Set<String> actualGroups = new HashSet<>(Arrays.asList(p.getRoles())); + Set<String> expectedGroups = new HashSet<>(Arrays.asList(groups)); + + Assert.assertEquals(expectedGroups.size(), actualGroups.size()); + Set<String> tmp = new HashSet<>(); + tmp.addAll(expectedGroups); + tmp.removeAll(actualGroups); + Assert.assertEquals(0, tmp.size()); + } + + + @BeforeClass + public static void createLDAP() throws Exception { + InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com"); + config.addAdditionalBindCredentials("cn=admin", "password"); + ldapServer = new InMemoryDirectoryServer(config); + + ldapServer.startListening(); + + try (LDAPConnection conn = ldapServer.getConnection()) { + + // Note: Only the DNs need attribute value escaping + AddRequest addBase = new AddRequest( + "dn: dc=example,dc=com", + "objectClass: top", + "objectClass: domain", + "dc: example"); + LDAPResult result = conn.processOperation(addBase); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addPeople = new AddRequest( + "dn: ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: organizationalUnit"); + result = conn.processOperation(addPeople); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addUserTest = new AddRequest( + "dn: cn=test,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: person", + "objectClass: organizationalPerson", + "cn: test", + "sn: Test", + "userPassword: test"); + result = conn.processOperation(addUserTest); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addUserTestSemicolon = new AddRequest( + "dn: cn=t\\;,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: person", + "objectClass: organizationalPerson", + "cn: t;", + "sn: Tsemicolon", + "userPassword: test"); + result = conn.processOperation(addUserTestSemicolon); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addUserTestAsterisk = new AddRequest( + "dn: cn=t*,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: person", + "objectClass: organizationalPerson", + "cn: t*", + "sn: Tasterisk", + "userPassword: test"); + result = conn.processOperation(addUserTestAsterisk); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addUserTestEquals = new AddRequest( + "dn: cn=t\\=,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: person", + "objectClass: organizationalPerson", + "cn: t=", + "sn: Tequals", + "userPassword: test"); + result = conn.processOperation(addUserTestEquals); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addGroupTest = new AddRequest( + "dn: cn=TestGroup,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: groupOfNames", + "cn: TestGroup", + "member: cn=test,ou=people,dc=example,dc=com", + "member: cn=t\\;,ou=people,dc=example,dc=com", + "member: cn=t\\*,ou=people,dc=example,dc=com"); + result = conn.processOperation(addGroupTest); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addGroupTest2 = new AddRequest( + "dn: cn=Test\\<Group*2,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: groupOfNames", + "cn: Test<Group*2", + "member: cn=t\\=,ou=people,dc=example,dc=com"); + result = conn.processOperation(addGroupTest2); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addGroupTest3 = new AddRequest( + "dn: cn=Test\\>Group*3,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: groupOfNames", + "cn: Test>Group*3", + "member: cn=Test\\<Group*2,ou=people,dc=example,dc=com"); + result = conn.processOperation(addGroupTest3); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addPeopleSub = new AddRequest( + "dn: ou=s\\;ub,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: organizationalUnit"); + result = conn.processOperation(addPeopleSub); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addUserTestSub = new AddRequest( + "dn: cn=testsub,ou=s\\;ub,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: person", + "objectClass: organizationalPerson", + "cn: testsub", + "sn: Testsub", + "userPassword: test"); + result = conn.processOperation(addUserTestSub); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + + AddRequest addGroupTest4 = new AddRequest( + "dn: cn=TestGroup4,ou=s\\;ub,ou=people,dc=example,dc=com", + "objectClass: top", + "objectClass: groupOfNames", + "cn: TestGroup4", + "member: cn=testsub,ou=s\\;ub,ou=people,dc=example,dc=com"); + result = conn.processOperation(addGroupTest4); + Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode()); + } + } + + + @AfterClass + public static void destroyLDAP() { + ldapServer.shutDown(true); + } +} Index: apache-tomcat-9.0.43-src/webapps/docs/changelog.xml =================================================================== --- apache-tomcat-9.0.43-src.orig/webapps/docs/changelog.xml +++ apache-tomcat-9.0.43-src/webapps/docs/changelog.xml @@ -110,6 +110,10 @@ <bug>65106</bug>: Fix the ConfigFileLoader handling of file URIs when running under a security manager on some JREs. (markt) </fix> + <scode> + Expand coverage of unit tests for JNDIRealm using the UnboundID LDAP SDK + for Java. (markt) + </scode> </changelog> </subsection> <subsection name="Coyote"> @@ -576,6 +580,11 @@ <a href="https://bugs.openjdk.java.net/browse/JDK-8255854">JRE bug</a>. (markt) </fix> + <fix> + Fix JNDIRealm pooling problems retrying on another bad connection. Any + retries are made on a new connection, just like with the single + connection scenario. (remm) + </fix> </changelog> </subsection> </section>
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