File c3p0-java-e14cbd.patch of Package c3p0.43042
From e14cbd8166e423e2e9a9d6f08b2add3433492d6e Mon Sep 17 00:00:00 2001
From: Steve Waldman <swaldman@mchange.com>
Date: Mon, 1 Jan 2024 00:00:00 +0000
Subject: Reimplement userOverridesAsString without relying on unnecessarily
dangerous Java Serialization.
--- c3p0-0.9.5.5.src/src/java/com/mchange/v2/c3p0/cfg/C3P0Config.java.orig
+++ c3p0-0.9.5.5.src/src/java/com/mchange/v2/c3p0/cfg/C3P0Config.java
@@ -46,6 +46,7 @@
import java.lang.reflect.Method;
import com.mchange.v1.lang.BooleanUtils;
import com.mchange.v2.c3p0.C3P0Registry;
+import com.mchange.v2.csv.MalformedCsvException;
//all internal maps should be HashMaps (the implementation presumes HashMaps)
@@ -474,7 +475,7 @@
return (out.isEmpty() ? null : out );
}
- public static String getUserOverridesAsString(String configName) throws IOException
+ public static String getUserOverridesAsString(String configName) throws IOException, MalformedCsvException
{
Map userOverrides = getUserOverrides( configName );
if (userOverrides == null)
--- c3p0-0.9.5.5.src/src/java/com/mchange/v2/c3p0/impl/C3P0ImplUtils.java.orig
+++ c3p0-0.9.5.5.src/src/java/com/mchange/v2/c3p0/impl/C3P0ImplUtils.java
@@ -36,13 +36,14 @@
package com.mchange.v2.c3p0.impl;
import java.beans.*;
+import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import com.mchange.v2.c3p0.*;
import com.mchange.v2.c3p0.cfg.*;
-import java.io.IOException;
+import com.mchange.v2.csv.*;
import java.sql.Connection;
import java.sql.SQLException;
import com.mchange.lang.ByteUtils;
@@ -299,6 +300,12 @@
private final static String HASM_HEADER = "HexAsciiSerializedMap";
+ /*
+ // Java Serialization-based userOverridesAsString format creates an unnecessary attack surface for
+ // placing malicious objects in the serialized format and provoking deserialization.
+ //
+ // We'll transition to a simpler, less dangerous format.
+
public static String createUserOverridesAsString( Map userOverrides ) throws IOException
{
StringBuffer sb = new StringBuffer();
@@ -320,6 +327,111 @@
else
return Collections.EMPTY_MAP;
}
+ */
+
+ // Quote a single CSV item: wrap in double-quotes and escape interior double-quotes as ""
+ private static String quoteCsvItem( String s )
+ {
+ if (s == null) return "\"\"";
+ return "\"" + s.replace("\"", "\"\"") + "\"";
+ }
+
+ // Generate a CSV line from an array of items, all quoted, without a line terminator
+ private static String generateCsvLine( String[] items )
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < items.length; ++i)
+ {
+ if (i > 0) sb.append(',');
+ sb.append( quoteCsvItem( items[i] ) );
+ }
+ return sb.toString();
+ }
+
+ // we serialize user overrides to "ragged CSV".
+ // CSV lines containing only a single element are interpreted as the user for whom we are overriding config
+ // lines following containing two elements are the config param overrides for that user.
+ public static String createUserOverridesAsString( Map userOverrides ) throws IOException, MalformedCsvException
+ {
+ Writer w = new StringWriter();
+ for (Object o : userOverrides.keySet())
+ {
+ // we don't check the type. We're treating this as basically an assertion, in our old-school, loosely typed Java
+ // we'll let the user see a ClassCastException if something has been messed with
+ String user = (String) o;
+ w.append( quoteCsvItem( user ) );
+ w.append("\r\n");
+ Map userProps = (Map) userOverrides.get(user);
+ String[] propNamePropValAsString = new String[2];
+ for (Object pn : userProps.keySet())
+ {
+ propNamePropValAsString[0] = (String) pn;
+ propNamePropValAsString[1] = (String) userProps.get(pn);
+ w.append( generateCsvLine( propNamePropValAsString ) );
+ w.append("\r\n");
+ }
+ }
+ return w.toString();
+ }
+
+ private static Map parseSingleUserMap(String userOverridesAsString, BufferedReader br, String[] nextUserHolder) throws IOException, MalformedCsvException
+ {
+ Map out = new HashMap();
+ nextUserHolder[0] = null;
+ String line = FastCsvUtils.csvReadLine(br);
+ if (line == null)
+ {
+ nextUserHolder[0] = null;
+ return Collections.EMPTY_MAP;
+ }
+ else
+ {
+ do
+ {
+ String[] items = FastCsvUtils.splitRecord( line );
+ switch ( items.length )
+ {
+ case 2: // this is an expected property override line
+ out.put(items[0],items[1]);
+ break;
+ case 1: // this is the next user name
+ nextUserHolder[0] = items[0];
+ break;
+ default:
+ throw new IOException("Unexpected CSV line in userOverridesAsString ('" + line + "'). All line should have 1 or 2 items:\r\n" + userOverridesAsString);
+ }
+ }
+ while (nextUserHolder[0] == null && (line = FastCsvUtils.csvReadLine(br)) != null); // either EOL or discovery of next user terminates
+ return Collections.unmodifiableMap(out);
+ }
+ }
+
+ public static Map parseUserOverridesAsString( String userOverridesAsString ) throws IOException, MalformedCsvException
+ {
+ if ( userOverridesAsString == null )
+ return Collections.EMPTY_MAP;
+ String[] nextUserHolder = new String[1];
+ BufferedReader br = new BufferedReader(new StringReader(userOverridesAsString));
+ String line = FastCsvUtils.csvReadLine(br);
+ if ( line == null )
+ return Collections.EMPTY_MAP;
+ else
+ {
+ Map out = new HashMap();
+ String[] items = FastCsvUtils.splitRecord(line);
+ if (items.length != 1)
+ throw new IOException("Cannot parse userOverridesAsString, one element line naming the user should come before other data:\r\n" + userOverridesAsString);
+ String username = items[0];
+ do
+ {
+ Map overrides = parseSingleUserMap(userOverridesAsString, br, nextUserHolder);
+ out.put(username, overrides);
+ username = nextUserHolder[0];
+ }
+ while (username != null);
+ return Collections.unmodifiableMap(out);
+ }
+ }
/**
* never intended to be called. we just want a compiler error if somehow we are building/code-generating