Skip to content

Commit

Permalink
IGNITE-20654 Forbid H2 connection "init" setting (#10997)
Browse files Browse the repository at this point in the history
  • Loading branch information
timoninmaxim authored Oct 17, 2023
1 parent 63476a7 commit ccab242
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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<String> knownSettings = (HashSet<String>)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.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Integer, Integer> cacheConfiguration() throws Exception {
CacheJdbcPojoStoreFactory<Integer, Integer> 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<DataSource> reflectionFactory = new IgniteReflectionFactory<>(org.h2.jdbcx.JdbcDataSource.class);
reflectionFactory.setProperties(F.asMap("url", url));

return new CacheConfiguration<Integer, Integer>(DEFAULT_CACHE_NAME)
.setCacheStoreFactory(factory.setDataSourceFactory(reflectionFactory));
}

/** */
private File checkFile() throws IgniteCheckedException {
return Paths.get(U.defaultWorkDirectory(), "init_check").toFile();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -255,6 +256,7 @@
GridCacheQuerySqlFieldInlineSizeSelfTest.class,
IgniteSqlParameterizedQueryTest.class,
H2ConnectionLeaksSelfTest.class,
H2ConnectionSettingsTest.class,
IgniteCheckClusterStateBeforeExecuteQueryTest.class,
OptimizedMarshallerIndexNameTest.class,
SqlSystemViewsSelfTest.class,
Expand Down

1 comment on commit ccab242

@Siebene
Copy link

@Siebene Siebene commented on ccab242 Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have added a "Security Model" section to our documentation [1].
It explains why we don't consider this an issue.

[1] 7b8b4ba

Please sign in to comment.