package burp;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.sqlite.SQLiteConfig;
import com.sun.webkit.ThemeClient;
/**
* Handles SQLite database access
*/
class Database {
private Config config;
private Connection conn = null;
private PreparedStatement pstmt = null;
private PrintWriter stdErr;
private PrintWriter stdOut;
private final String moduleName = "DB";
private final String connPrefix = "jdbc:sqlite:";
Database(BurpExtender b)
{
config = b.getConfig();
stdErr = b.getStdErr();
stdOut = b.getStdOut();
try
{
Class.forName("org.sqlite.JDBC"); // load the JDBC Driver
}
catch (ClassNotFoundException e)
{
stdErr.println(e.getMessage());
}
}
/**
* open a different database file after a config change
*/
void changeFile()
{
close();
conn = getConnection();
}
/**
* close the database connection
*/
boolean close()
{
try
{
if (conn != null)
{
conn.close();
}
return true;
}
catch (SQLException e)
{
stdErr.println(e.getMessage());
return false;
}
}
/**
* TODO: this might need some tweaking
*/
protected void finalize() throws Throwable
{
try
{
if (conn != null)
{
conn.close();
}
}
finally
{
super.finalize();
}
}
/**
* open and return database connections
*/
private Connection getConnection()
{
Connection connection;
SQLiteConfig sc = new SQLiteConfig();
sc.setEncoding(SQLiteConfig.Encoding.UTF_8);
try
{
connection = DriverManager.getConnection(connPrefix
+ config.databaseFilename, sc.toProperties());
stdOut.println(moduleName + ": Opened database file: " + config.databaseFilename);
}
catch (SQLException e)
{
stdErr.println(e.getMessage());
return null;
}
return connection;
}
/**
* initialize the database
*/
boolean init()
{
Statement stmt = null;
try
{
if (conn == null)
{
conn = getConnection();
}
stmt = conn.createStatement();
stmt.setQueryTimeout(30);
stdOut.println(" + Rebuilding all DB tables.");
String sql_dropTables = "DROP TABLE IF EXISTS params; DROP TABLE IF EXISTS hashes; DROP TABLE IF EXISTS algorithms;";
stmt.executeUpdate(sql_dropTables);
String sql_createAlgoTable = "CREATE TABLE algorithms (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, Name TEXT NOT NULL)";
stmt.executeUpdate(sql_createAlgoTable);
String sql_createParamTable = "CREATE TABLE params (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, value TEXT NOT NULL)";
stmt.executeUpdate(sql_createParamTable);
String sql_createHashTable = "CREATE TABLE hashes (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, algorithmID INTEGER NOT NULL, paramID INTEGER, value TEXT NOT NULL)";
stmt.executeUpdate(sql_createHashTable);
stdOut.println(" + Adding " + config.hashAlgorithms.size() + " hash algorithms to DB.");
Collections.reverse(config.hashAlgorithms); //so the db has ascending order
String sql_insertAlgo = "INSERT OR REPLACE INTO algorithms(name, ID) VALUES (?, ?)";
for (HashAlgorithm algo : config.hashAlgorithms)
{
pstmt = conn.prepareStatement(sql_insertAlgo);
pstmt.setString(1, algo.name.text);
pstmt.setString(2, Integer.toString(algo.id));
pstmt.executeUpdate();
stdOut.println(" + Adding Hash Algorithm to DB: " + algo.name.text + ", " + algo.id);
}
Collections.reverse(config.hashAlgorithms); //back to descending order for hash searching
stdOut.println(moduleName + ": Database initialized.");
return true;
}
catch (SQLException e)
{
stdErr.println(e.getMessage());
return false;
}
catch (Exception ex)
{
stdErr.println(ex);
return false;
}
}
boolean saveParam(String paramValue)
{
int paramId = getParamId(paramValue);
if (paramId > 0)
{
//if (config.debug) stdOut.println(moduleName + ": Not saving parameter (" + paramValue +") since it's already in the db at index = " + paramId);
return false;
}
try
{
if (conn == null)
{
conn = getConnection();
}
String sql_insertParam = "INSERT OR REPLACE INTO params(value) VALUES (?)";
pstmt = conn.prepareStatement(sql_insertParam);
pstmt.setString(1, paramValue);
pstmt.executeUpdate();
stdOut.println(moduleName + ": Saving Discovered Parameter Value: " + paramValue);
return true;
}
catch (SQLException e)
{
stdErr.println(e.getMessage());
return false;
}
}
int getParamId(String paramValue)
{
try
{
if (conn == null)
{
conn = getConnection();
}
String sql_paramExists = "SELECT * from params where value = ?";
pstmt = conn.prepareStatement(sql_paramExists);
pstmt.setString(1, paramValue);
ResultSet rs = pstmt.executeQuery();
if (!rs.next())
{
return 0;
}
int id = rs.getInt("id");
if (config.debug) stdOut.println(moduleName + ": Found '" + paramValue + "' in the db at index=" + id);
return id;
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQLException: " + e);
return -1;
}
}
String getParamByHash(HashRecord hash)
{
try
{
if (conn == null)
{
conn = getConnection();
}
String sql_paramExists = "select params.value from hashes inner join params on hashes.paramID=params.ID where hashes.algorithmid = ? and hashes.value = ?";
pstmt = conn.prepareStatement(sql_paramExists);
pstmt.setString(1, Integer.toString(hash.algorithm.id));
pstmt.setString(2, hash.getNormalizedRecord());
ResultSet rs = pstmt.executeQuery();
if (!rs.next())
{
return null;
}
String paramValue = rs.getString("value");
if (config.debug) stdOut.println(moduleName + ": Match '" + paramValue + "' for '" + hash.getNormalizedRecord() +"'");
return paramValue;
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQLException: " + e);
return null;
}
}
boolean saveParamWithHash(ParameterWithHash parmWithHash)
{
int paramId = getParamId(parmWithHash.parameter.value);
if (paramId <= 0)
{
if (config.debug) stdOut.println(moduleName + ": Cannot save hash " + parmWithHash.hashedValue + " until the following parameter is saved " + parmWithHash.parameter.value);
saveParam(parmWithHash.parameter.value);
paramId = getParamId(parmWithHash.parameter.value);
}
try
{
if (conn == null)
{
conn = getConnection();
}
int algorithmId = config.getHashId(parmWithHash.algorithm);
if (algorithmId <= 0)
{
stdErr.println(moduleName + ": Could not locate Algorithm ID for " + parmWithHash.algorithm);
return false;
}
String sql_insertHash = "INSERT OR REPLACE INTO hashes(algorithmID, paramID, value) VALUES (?, ?, ?)";
pstmt = conn.prepareStatement(sql_insertHash);
pstmt.setString(1, Integer.toString(algorithmId));
pstmt.setString(2, Integer.toString(paramId));
pstmt.setString(3, parmWithHash.hashedValue.toLowerCase());
pstmt.executeUpdate();
if (config.debug) stdOut.println(moduleName + ": Saved " + parmWithHash.algorithm.text + " hash in db: " + parmWithHash.parameter.value + ":" + parmWithHash.hashedValue);
return true;
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQLException: " + e);
return false;
}
}
boolean saveHash(HashRecord hash)
{
if (getHashIdByValue(hash.getNormalizedRecord()) > 0)
{
//stdOut.println(moduleName + ": Not saving hash (" + hash.getNormalizedRecord() + ") since it's already in the db.");
return false;
}
try
{
if (conn == null)
{
conn = getConnection();
}
String sql_insertHash = "INSERT OR REPLACE INTO hashes(algorithmID, value) VALUES (?, ?)";
pstmt = conn.prepareStatement(sql_insertHash);
pstmt.setString(1, Integer.toString(hash.algorithm.id));
pstmt.setString(2, hash.getNormalizedRecord());
pstmt.executeUpdate();
stdOut.println(moduleName + ": Saving " + hash.algorithm.name.text + " hash of unknown source value in db: " + hash.getNormalizedRecord());
return true;
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQLException: " + e);
return false;
}
}
int getHashIdByValue(String hashedValue)
{
try
{
if (conn == null)
{
conn = getConnection();
}
String sql_hashExists = "SELECT * from hashes where value = ?";
pstmt = conn.prepareStatement(sql_hashExists);
pstmt.setString(1, hashedValue);
ResultSet rs = pstmt.executeQuery();
if (!rs.next())
{
return 0;
}
int id = rs.getInt("id");
if (config.debug) stdOut.println(moduleName + ": Found '" + hashedValue + "' in the db at index=" + id);
return id;
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQLException: " + e);
return -1;
}
}
/**
* TODO: verify presence of all tables? (params, hashes, etc.) < Yes please, but !MVP [TM]
*/
boolean verify()
{
Statement stmt = null;
ResultSet rs = null;
try
{
if (conn == null)
{
conn = getConnection();
}
stmt = conn.createStatement();
stmt.setQueryTimeout(30);
String sql_tableCheck = "SELECT name FROM sqlite_master WHERE type='table' AND name='params';";
rs = stmt.executeQuery(sql_tableCheck);
boolean x = false;
while (rs.next())
{
x = true;
}
return x;
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQLException: " + e);
return false;
}
}
/**
* TODO: This is to return the list of parameters that were saved without HashAlgorithm.Name hashes.
* In other words, pump this list out, hash them against 'algorithm' and save them back to the DB for
* future comparisons.
* @param algorithm
* @return
*/
public List<String> getParamsWithoutHashType(HashAlgorithm algorithm)
{
List<String> params = new ArrayList<>();
//TODO: Need to fix this query - want to find all the params that don't have a hash table entry with algorithm ID matching the algorithm passed to this method:
String sql_selectMissing = "select ID, VALUE from params where ID not in (select paramID from hashes where hashes.algorithmID = ?)";
try
{
pstmt = conn.prepareStatement(sql_selectMissing);
pstmt.setString(1, Integer.toString(algorithm.id));
ResultSet rs = pstmt.executeQuery();
while (rs.next())
{
//int paramId = rs.getInt("id");
String value = rs.getString("value");
params.add(value);
}
}
catch (SQLException e)
{
stdErr.println(moduleName + ": SQL Exception: " + e);
}
return params;
}
}