From ccab242a58858d433aa8ca70aaaaf6866a2ed6af Mon Sep 17 00:00:00 2001 From: Maksim Timonin Date: Tue, 17 Oct 2023 10:17:31 +0300 Subject: [PATCH] IGNITE-20654 Forbid H2 connection "init" setting (#10997) --- .../query/h2/ConnectionManager.java | 37 ++++++++ .../processors/query/h2/IgniteH2Indexing.java | 10 +++ .../cache/index/H2ConnectionSettingsTest.java | 87 +++++++++++++++++++ .../IgniteBinaryCacheQueryTestSuite3.java | 2 + 4 files changed, 136 insertions(+) create mode 100644 modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionSettingsTest.java diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java index 098e552d21cc7..d68fbc26f0509 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ConnectionManager.java @@ -17,11 +17,14 @@ package org.apache.ignite.internal.processors.query.h2; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.ignite.IgniteCheckedException; @@ -35,6 +38,7 @@ import org.apache.ignite.internal.util.GridBusyLock; import org.apache.ignite.internal.util.typedef.internal.U; import org.h2.api.JavaObjectSerializer; +import org.h2.engine.ConnectionInfo; import org.h2.engine.Database; import org.h2.jdbc.JdbcConnection; import org.h2.store.DataHandler; @@ -76,6 +80,9 @@ public class ConnectionManager { // H2 DbSettings. System.setProperty("h2.optimizeTwoEquals", "false"); // Makes splitter fail on subqueries in WHERE. System.setProperty("h2.dropRestrict", "false"); // Drop schema with cascade semantics. + + // Forbid vulnerable settings. + forbidH2DbSettings("INIT"); } /** The period of clean up the statement cache. */ @@ -351,4 +358,34 @@ void setH2Serializer(JavaObjectSerializer serializer) { if (dataNhd != null && dataNhd instanceof Database) DB_JOBJ_SERIALIZER.set((Database)dataNhd, serializer); } + + /** + * Forbids specified settings from KNOWN_SETTINGS for security reasons. + * + * @param settings Settings to forbid. + */ + private static void forbidH2DbSettings(String... settings) { + try { + Field knownSettingsField = ConnectionInfo.class.getDeclaredField("KNOWN_SETTINGS"); + Field modifiers = Field.class.getDeclaredField("modifiers"); + + modifiers.setAccessible(true); + modifiers.setInt(knownSettingsField, knownSettingsField.getModifiers() & ~Modifier.FINAL); + + knownSettingsField.setAccessible(true); + + HashSet knownSettings = (HashSet)knownSettingsField.get(null); + + for (String s: settings) + knownSettings.remove(s); + + modifiers.setInt(knownSettingsField, knownSettingsField.getModifiers() & Modifier.FINAL); + modifiers.setAccessible(false); + + knownSettingsField.setAccessible(false); + } + catch (Exception e) { + // No-op. + } + } } diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java index e9ddd2e0434c7..0ca832360e935 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java @@ -194,6 +194,16 @@ public class IgniteH2Indexing implements GridQueryIndexing { private final boolean updateInTxAllowed = Boolean.getBoolean(IgniteSystemProperties.IGNITE_ALLOW_DML_INSIDE_TRANSACTION); + static { + // Required to skip checks of forbidden H2 settings, otherwise Ignite fails to start. + // + // Note, H2 system properties must be overriden here, because the properties are finalized while the class + // org.h2.engine.SysProperties is loaded in the IgniteH2Indexing.start(...) method. + // + // @see ConnectionManager#forbidH2DbSettings(String...) + System.setProperty("h2.check", "false"); + } + /** Logger. */ @LoggerResource private IgniteLogger log; diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionSettingsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionSettingsTest.java new file mode 100644 index 0000000000000..3ac7225119129 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2ConnectionSettingsTest.java @@ -0,0 +1,87 @@ +/* + * 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.ignite.internal.processors.cache.index; + +import java.io.File; +import java.nio.file.Paths; +import javax.sql.DataSource; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.IgniteReflectionFactory; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; + +/** */ +public class H2ConnectionSettingsTest extends GridCommonAbstractTest { + /** {@inheritDoc} */ + @Override protected void afterTest() { + stopAllGrids(); + } + + /** */ + @Test + public void testInitForbidden() throws Exception { + IgniteConfiguration cfg = getConfiguration(getTestIgniteInstanceName()) + .setCacheConfiguration(cacheConfiguration()); + + GridTestUtils.assertThrowsWithCause(() -> startGrid(cfg), org.h2.jdbc.JdbcSQLException.class); + + assertFalse(checkFile().exists()); + } + + /** */ + @Test + public void testInitForbiddenInDynamic() throws Exception { + IgniteEx ign = startGrid(); + + IgniteEx cln = startClientGrid(1); + + GridTestUtils.assertThrowsWithCause(() -> cln.createCache(cacheConfiguration()), org.h2.jdbc.JdbcSQLException.class); + + assertFalse(checkFile().exists()); + + assertNotNull(ign.createCache(DEFAULT_CACHE_NAME)); + } + + /** */ + private CacheConfiguration cacheConfiguration() throws Exception { + CacheJdbcPojoStoreFactory factory = new CacheJdbcPojoStoreFactory<>(); + + checkFile().delete(); + + String init = "//javascript\njava.lang.Runtime.getRuntime().exec(\"touch " + checkFile().getAbsolutePath() + " \")"; + String url = "jdbc:h2:mem:test;init=CREATE TRIGGER h BEFORE SELECT ON INFORMATION_SCHEMA.CATALOGS AS '" + init + "'"; + + IgniteReflectionFactory reflectionFactory = new IgniteReflectionFactory<>(org.h2.jdbcx.JdbcDataSource.class); + reflectionFactory.setProperties(F.asMap("url", url)); + + return new CacheConfiguration(DEFAULT_CACHE_NAME) + .setCacheStoreFactory(factory.setDataSourceFactory(reflectionFactory)); + } + + /** */ + private File checkFile() throws IgniteCheckedException { + return Paths.get(U.defaultWorkDirectory(), "init_check").toFile(); + } +} diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java index 883274ad90488..286dd897116c5 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite3.java @@ -87,6 +87,7 @@ import org.apache.ignite.internal.processors.cache.index.BasicSqlTypesIndexTest; import org.apache.ignite.internal.processors.cache.index.DateIndexKeyTypeTest; import org.apache.ignite.internal.processors.cache.index.H2ConnectionLeaksSelfTest; +import org.apache.ignite.internal.processors.cache.index.H2ConnectionSettingsTest; import org.apache.ignite.internal.processors.cache.index.H2RowCachePageEvictionTest; import org.apache.ignite.internal.processors.cache.index.H2RowCacheSelfTest; import org.apache.ignite.internal.processors.cache.index.H2RowExpireTimeIndexSelfTest; @@ -255,6 +256,7 @@ GridCacheQuerySqlFieldInlineSizeSelfTest.class, IgniteSqlParameterizedQueryTest.class, H2ConnectionLeaksSelfTest.class, + H2ConnectionSettingsTest.class, IgniteCheckClusterStateBeforeExecuteQueryTest.class, OptimizedMarshallerIndexNameTest.class, SqlSystemViewsSelfTest.class,