Improper Input Validation in GeoServer
Description
GeoServer is an open source software server written in Java that allows users to share and edit geospatial data. The GeoServer security mechanism can perform an unchecked JNDI lookup, which in turn can be used to perform class deserialization and result in arbitrary code execution. The same can happen while configuring data stores with data sources located in JNDI, or while setting up the disk quota mechanism. In order to perform any of the above changes, the attack needs to have obtained admin rights and use either the GeoServer GUI, or its REST API. The lookups are going to be restricted in GeoServer 2.21.0, 2.20.4, 1.19.6. Users unable to upgrade should restrict access to the geoserver/web and geoserver/rest via a firewall and ensure that the GeoWebCache is not remotely accessible.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.geoserver:gs-mainMaven | >= 2.20.0, < 2.20.4 | 2.20.4 |
org.geoserver:gs-mainMaven | < 2.19.6 | 2.19.6 |
Affected products
1Patches
1b94a69943992[GEOS-10460] Centralize JNDI lookups
5 files changed · +27 −60
src/community/jdbcconfig/src/main/java/org/geoserver/jdbcloader/DataSourceFactoryBean.java+1 −26 modified@@ -11,7 +11,6 @@ import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; -import javax.naming.Context; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; @@ -26,33 +25,10 @@ public class DataSourceFactoryBean implements FactoryBean<DataSource>, Disposabl private static final Logger LOGGER = Logging.getLogger(DataSourceFactoryBean.class); JDBCLoaderProperties config; - Context jndiCtx; DataSource dataSource; - private static Context getJNDI(JDBCLoaderProperties config) { - if (config.isEnabled() && config.getJndiName().isPresent()) { - try { - return GeoTools.getInitialContext(GeoTools.getDefaultHints()); - } catch (NamingException ex) { - LOGGER.log( - Level.WARNING, - "Could not get JNDI Context, will not use JNDI to locate DataSource", - ex); - return null; - } - } else { - // Don't bother trying to get a JNDI context if JNDI lookup isn't needed. - return null; - } - } - public DataSourceFactoryBean(JDBCLoaderProperties config) { - this(config, getJNDI(config)); - } - - public DataSourceFactoryBean(JDBCLoaderProperties config, Context jndiCtx) { this.config = config; - this.jndiCtx = jndiCtx; } @Override @@ -121,11 +97,10 @@ protected BasicDataSource createBasicDataSource() { /** Try to lookup a configured DataSource using JNDI. */ protected Optional<DataSource> getJNDIDataSource(Optional<String> name) { - if (jndiCtx == null) return Optional.absent(); if (name.isPresent()) { try { - Optional<DataSource> ds = Optional.of((DataSource) jndiCtx.lookup(name.get())); + Optional<DataSource> ds = Optional.of((DataSource) GeoTools.jndiLookup(name.get())); if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, "JDBCLoader using JNDI DataSource {0}", name.get()); }
src/community/jdbcconfig/src/test/java/org/geoserver/jdbcconfig/internal/DataSourceFactoryBeanTest.java+11 −9 modified@@ -16,12 +16,13 @@ import com.google.common.base.Optional; import java.sql.Connection; import java.sql.DatabaseMetaData; -import javax.naming.Context; +import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.easymock.EasyMock; import org.geoserver.jdbcloader.DataSourceFactoryBean; +import org.geotools.util.factory.GeoTools; import org.junit.Test; /** @author Kevin Smith, OpenGeo */ @@ -31,7 +32,6 @@ public class DataSourceFactoryBeanTest { public void testBasic() throws Exception { final BasicDataSource ds = EasyMock.createMock(BasicDataSource.class); JDBCConfigProperties config = EasyMock.createMock(JDBCConfigProperties.class); - Context jndi = EasyMock.createMock(Context.class); expect(config.isEnabled()).andReturn(true); expectJndi(config, null); @@ -73,10 +73,10 @@ public void testBasic() throws Exception { expectLastCall(); expectVerifyConnect(ds); - replay(ds, config, jndi); + replay(ds, config); DataSourceFactoryBean fact = - new DataSourceFactoryBean(config, jndi) { + new DataSourceFactoryBean(config) { @Override protected BasicDataSource createBasicDataSource() { @@ -92,7 +92,7 @@ protected BasicDataSource createBasicDataSource() { // Check that the same DataSource is returned on subsequent calls without any changes assertThat(fact.getObject(), is((DataSource) ds)); - verify(ds, config, jndi); + verify(ds, config); // Check that destruction properly closes the DataSource reset(ds); @@ -107,7 +107,8 @@ protected BasicDataSource createBasicDataSource() { public void testJNDI() throws Exception { DataSource ds = EasyMock.createMock(DataSource.class); JDBCConfigProperties config = EasyMock.createMock(JDBCConfigProperties.class); - Context jndi = EasyMock.createMock(Context.class); + InitialContext jndi = EasyMock.createMock(InitialContext.class); + GeoTools.init(jndi); expect(config.isEnabled()).andReturn(true); expectJndi(config, "java:comp/env/jdbc/test"); @@ -118,7 +119,7 @@ public void testJNDI() throws Exception { expectVerifyConnect(ds); replay(ds, config, jndi); - DataSourceFactoryBean fact = new DataSourceFactoryBean(config, jndi); + DataSourceFactoryBean fact = new DataSourceFactoryBean(config); // Check that we get the DataSource assertThat(fact.getObject(), is((DataSource) ds)); @@ -143,7 +144,8 @@ public void testJNDI() throws Exception { public void testJNDIFail() throws Exception { final BasicDataSource ds = EasyMock.createMock(BasicDataSource.class); JDBCConfigProperties config = EasyMock.createMock(JDBCConfigProperties.class); - Context jndi = EasyMock.createMock(Context.class); + InitialContext jndi = EasyMock.createMock(InitialContext.class); + GeoTools.init(jndi); expect(config.isEnabled()).andReturn(true); expectJndi(config, "java:comp/env/jdbc/test"); @@ -190,7 +192,7 @@ public void testJNDIFail() throws Exception { replay(ds, config, jndi); DataSourceFactoryBean fact = - new DataSourceFactoryBean(config, jndi) { + new DataSourceFactoryBean(config) { @Override protected BasicDataSource createBasicDataSource() {
src/community/taskmanager/core/src/main/java/org/geoserver/taskmanager/external/impl/PostgisJndiDbSourceImpl.java+1 −10 modified@@ -11,7 +11,6 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; -import javax.naming.Context; import javax.naming.NamingException; import javax.sql.DataSource; import org.geoserver.taskmanager.external.DbSource; @@ -58,17 +57,9 @@ public void setSchema(String schema) { @Override public DataSource getDataSource() throws SQLException { - Context ctx = null; DataSource ds = null; - - try { - ctx = GeoTools.getInitialContext(GeoTools.getDefaultHints()); - } catch (NamingException e) { - throw new IllegalStateException(e); - } - try { - ds = (DataSource) ctx.lookup(jndiName); + ds = (DataSource) GeoTools.jndiLookup(jndiName); } catch (NamingException e) { throw new SQLException(e); }
src/security/jdbc/src/main/java/org/geoserver/security/jdbc/AbstractJDBCService.java+2 −4 modified@@ -18,8 +18,6 @@ import java.util.Properties; import java.util.StringTokenizer; import java.util.logging.Logger; -import javax.naming.Context; -import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; @@ -31,6 +29,7 @@ import org.geoserver.security.impl.AbstractGeoServerSecurityService; import org.geoserver.security.jdbc.config.JDBCSecurityServiceConfig; import org.geoserver.util.IOUtils; +import org.geotools.util.factory.GeoTools; /** * JDBC base implementation for common used methods @@ -56,8 +55,7 @@ public void initializeDSFromConfig(SecurityNamedServiceConfig namedConfig) throw if (config.isJndi()) { String jndiName = config.getJndiName(); try { - Context initialContext = new InitialContext(); - datasource = (DataSource) initialContext.lookup(jndiName); + datasource = (DataSource) GeoTools.jndiLookup(jndiName); } catch (NamingException e) { throw new IOException(e); }
src/web/security/jdbc/src/main/java/org/geoserver/security/web/jdbc/JDBCConnectionPanel.java+12 −11 modified@@ -10,8 +10,6 @@ import java.sql.DriverManager; import java.util.logging.Level; import java.util.logging.Logger; -import javax.naming.Context; -import javax.naming.InitialContext; import javax.sql.DataSource; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; @@ -27,6 +25,7 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.StringResourceModel; import org.geoserver.security.jdbc.config.JDBCSecurityServiceConfig; +import org.geotools.util.factory.GeoTools; import org.geotools.util.logging.Logging; /** @@ -179,16 +178,18 @@ public void test() throws Exception { // models ((FormComponent) get("jndiName")).processInput(); - Context initialContext = new InitialContext(); - try { - DataSource datasource = - (DataSource) - initialContext.lookup( - get("jndiName").getDefaultModelObjectAsString()); - try (Connection con = datasource.getConnection()) {} - } finally { - initialContext.close(); + Object lookedUp = GeoTools.jndiLookup(get("jndiName").getDefaultModelObjectAsString()); + if (lookedUp == null) + throw new IllegalArgumentException( + "Failed to look up an object from JNDI at the given location"); + if (!(lookedUp instanceof DataSource)) { + LOGGER.log( + Level.WARNING, + "Was trying to look up a DataSource in JNDI, but got this instead: " + + lookedUp); + throw new IllegalArgumentException("JNDI lookup did not provide a DataSource"); } + try (Connection con = ((DataSource) lookedUp).getConnection()) {} } } }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-4pm3-f52j-8gghghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-24847ghsaADVISORY
- github.com/geoserver/geoserver/commit/b94a69943992df999d627b21a4ed056fad4569f8ghsaWEB
- github.com/geoserver/geoserver/security/advisories/GHSA-4pm3-f52j-8gghghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.