/* * Copyright 2009-2012 Michael Dalton * * Licensed 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 jtaint; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; /* Supports MySql 4.0 - 6.0 (current) */ public final class MySqlValidator extends SqlValidator { private final boolean noBackslashEscapes; private final boolean ansiQuotes; public boolean isSqlOperator(char c) { return "!~@[]{}(),.;:+-*/%&<>=|^?".indexOf(c) != -1; } public boolean isSqlSpecial(char c) { return "\"'`".indexOf(c) != -1; } /** Parse a block comment. Nesting is not supported. Tainted data is allowed * within the comment, but may neither begin nor end the comment. */ public int parseMYBlockComment(String s, int offset) { int len = s.length(); Taint t = s.@internal@taint(); boolean isCommand = false; if (offset < len - 2 && s.charAt(offset+2) == '!') { if (t.get(offset+2)) SqlUtil.abortQuery(s, "Tainted block comment command"); isCommand = true; offset++; } /* Skip leading '/*' */ offset++; while (++offset < len) { char c = s.charAt(offset); if (isCommand && t.get(offset)) SqlUtil.abortQuery(s, "Tainted character in comment command"); if (c == '*' && offset != len-1 && s.charAt(offset + 1) == '/') { if (t.get(offset) || t.get(offset + 1)) SqlUtil.abortQuery(s, "Tainted comment end (*/)"); return offset + 1; } } SqlUtil.parseError(s, "Unterminated block comment"); return offset; } public void validateSqlQuery(String s) { int len; Taint t; if (!s.@internal@isTainted()) return; t = s.@internal@taint(); len = s.length(); for (int i = 0; i < len; i++) { char c = s.charAt(i); if (t.get(i)) { i = SqlParseUtil.parseTaintedValue(s, i, this); continue; } switch(c) { case '#': i = SqlParseUtil.parseLineComment(s, i, "#"); break; case '-': /* Possibly a line comment */ if (i < len - 2 && s.charAt(i+1) == '-' && isSqlWhitespace(s.charAt(i+2))) i = SqlParseUtil.parseLineComment(s, i, "--" + s.charAt(i+2)); break; case '/': /* Possibly a block comment */ if (i < len - 1 && s.charAt(i+1) == '*') { if (t.get(i+1)) SqlUtil.abortQuery(s, "Tainted block comment"); i = parseMYBlockComment(s, i); } break; case '\'': /* String Literal */ i = SqlParseUtil.parseStringLiteral(s, i, !noBackslashEscapes); break; case '"': if (ansiQuotes) i = SqlParseUtil.parseQuotedIdentifier(s, i); else i = SqlParseUtil.parseStringLiteral(s, i, !noBackslashEscapes); break; case '`': i = SqlParseUtil.parseQuotedIdentifier(s, i); break; default: break; } } } public MySqlValidator(Connection c, int dbMajor, int dbMinor) { String modes = ""; Statement st = null; try { String query= "select @@global.sql_mode"; st = c.createStatement(); ResultSet rs = st.executeQuery(query); while (rs.next()) modes += rs.getString(1); query = "select @@session.sql_mode"; rs = st.executeQuery(query); while (rs.next()) modes += rs.getString(1); st.close(); } catch (Throwable th) { Log.error(th); } if (modes.indexOf("NO_BACKSLASH_ESCAPES") >= 0) noBackslashEscapes = true; else noBackslashEscapes = false; if (modes.indexOf("ANSI_QUOTES") >= 0) ansiQuotes = true; else ansiQuotes = false; if (st != null) { try { st.close(); } catch (Throwable th) { } } } public MySqlValidator(boolean ansiQuotes, boolean noBackslashEscapes) { this.ansiQuotes = ansiQuotes; this.noBackslashEscapes = noBackslashEscapes; } }