Skip to content

Commit 353b66c

Browse files
Merge pull request #95 from abeluck/sql_injection
Fix potential SQL injection when keying the database
2 parents 261b760 + 2c113f6 commit 353b66c

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed

jni/Android.mk

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ endif
1616

1717
include $(CLEAR_VARS)
1818

19+
# expose the sqlcipher C API
20+
LOCAL_CFLAGS += -DSQLITE_HAS_CODEC
21+
1922
LOCAL_SRC_FILES:= \
2023
net_sqlcipher_database_SQLiteCompiledSql.cpp \
2124
net_sqlcipher_database_SQLiteDatabase.cpp \
@@ -37,6 +40,7 @@ LOCAL_C_INCLUDES += \
3740
$(EXTERNAL_PATH)/platform-system-core/include \
3841
$(LOCAL_PATH)/include \
3942
$(EXTERNAL_PATH)/platform-frameworks-base/include \
43+
$(EXTERNAL_PATH)/icu4c/common \
4044

4145
LOCAL_SHARED_LIBRARIES := \
4246
libcrypto \
@@ -53,8 +57,9 @@ LOCAL_LDLIBS += -ldl -llog
5357
LOCAL_LDLIBS += -lnativehelper -landroid_runtime -lutils -lbinder
5458
# these are build in the ../external section
5559

56-
#LOCAL_REQUIRED_MODULES += libsqlcipher libicuuc libicui18n
57-
LOCAL_LDLIBS += -lsqlcipher_android
60+
LOCAL_LDLIBS += -lsqlcipher_android
61+
LOCAL_LDFLAGS += -L../obj/local/$(TARGET_ARCH_ABI)
62+
LOCAL_LDLIBS += -licui18n -licuuc
5863

5964
ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
6065
LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK

jni/net_sqlcipher_database_SQLiteDatabase.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
#include <string.h>
3838
#include <sys/ioctl.h>
3939

40+
#include <unicode/utypes.h>
41+
#include <unicode/ucnv.h>
42+
#include <unicode/ucnv_err.h>
43+
4044
#include "sqlite3_exception.h"
4145
#include "sqlcipher_loading.h"
4246

@@ -104,6 +108,69 @@ int native_status(JNIEnv* env, jobject object, jint operation, jboolean reset)
104108
return value;
105109
}
106110

111+
void native_key_char(JNIEnv* env, jobject object, jcharArray jKey)
112+
{
113+
char *keyUtf8 = 0;
114+
int lenUtf8 = 0;
115+
jchar* keyUtf16 = 0;
116+
jsize lenUtf16 = 0;
117+
UErrorCode status = U_ZERO_ERROR;
118+
UConverter *encoding = 0;
119+
120+
sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
121+
122+
keyUtf16 = env->GetCharArrayElements(jKey, 0);
123+
lenUtf16 = env->GetArrayLength(jKey);
124+
125+
// no key, bailing out.
126+
if ( lenUtf16 == 0 ) goto done;
127+
128+
encoding = ucnv_open("UTF-8", &status);
129+
if( U_FAILURE(status) ) {
130+
throw_sqlite3_exception(env, "native_key_char: opening encoding converter failed");
131+
goto done;
132+
}
133+
134+
lenUtf8 = ucnv_fromUChars(encoding, NULL, 0, keyUtf16, lenUtf16, &status);
135+
status = (status == U_BUFFER_OVERFLOW_ERROR) ? U_ZERO_ERROR : status;
136+
if( U_FAILURE(status) ) {
137+
throw_sqlite3_exception(env, "native_key_char: utf8 length unknown");
138+
goto done;
139+
}
140+
141+
keyUtf8 = (char*) malloc(lenUtf8 * sizeof(char));
142+
ucnv_fromUChars(encoding, keyUtf8, lenUtf8, keyUtf16, lenUtf16, &status);
143+
if( U_FAILURE(status) ) {
144+
throw_sqlite3_exception(env, "native_key_char: utf8 conversion failed");
145+
goto done;
146+
}
147+
148+
if ( sqlite3_key(handle, keyUtf8, lenUtf8) != SQLITE_OK ) {
149+
throw_sqlite3_exception(env, handle);
150+
}
151+
152+
done:
153+
env->ReleaseCharArrayElements(jKey, keyUtf16, 0);
154+
if(encoding != 0) ucnv_close(encoding);
155+
if(keyUtf8 != 0) free(keyUtf8);
156+
}
157+
158+
void native_key_str(JNIEnv* env, jobject object, jstring jKey)
159+
{
160+
sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
161+
162+
char const * key = env->GetStringUTFChars(jKey, NULL);
163+
jsize keyLen = env->GetStringUTFLength(jKey);
164+
165+
if ( keyLen > 0 ) {
166+
int status = sqlite3_key(handle, key, keyLen);
167+
if ( status != SQLITE_OK ) {
168+
throw_sqlite3_exception(env, handle);
169+
}
170+
}
171+
env->ReleaseStringUTFChars(jKey, key);
172+
}
173+
107174
void native_rawExecSQL(JNIEnv* env, jobject object, jstring sql)
108175
{
109176
sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
@@ -492,6 +559,8 @@ static JNINativeMethod sMethods[] =
492559
{"setICURoot", "(Ljava/lang/String;)V", (void *)setICURoot},
493560
{"native_rawExecSQL", "(Ljava/lang/String;)V", (void *)native_rawExecSQL},
494561
{"native_status", "(IZ)I", (void *)native_status},
562+
{"native_key", "([C)V", (void *)native_key_char},
563+
{"native_key", "(Ljava/lang/String;)V", (void *)native_key_str},
495564
};
496565

497566
int register_android_database_SQLiteDatabase(JNIEnv *env)

src/net/sqlcipher/database/SQLiteDatabase.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public class SQLiteDatabase extends SQLiteClosable {
7777
public int status(int operation, boolean reset){
7878
return native_status(operation, reset);
7979
}
80-
80+
8181
public static void upgradeDatabaseFormatFromVersion1To2(File databaseToMigrate, String password) throws Exception {
8282

8383
File newDatabasePath = null;
@@ -90,7 +90,7 @@ public void postKey(SQLiteDatabase database){
9090
database.execSQL("PRAGMA cipher_default_use_hmac = on");
9191
}
9292
};
93-
93+
9494
try {
9595
newDatabasePath = File.createTempFile("temp", "db", databaseToMigrate.getParentFile());
9696
SQLiteDatabase source = SQLiteDatabase.openOrCreateDatabase(databaseToMigrate, password, null, hook);
@@ -920,15 +920,15 @@ public static SQLiteDatabase openDatabase(String path, String password, CursorFa
920920
new WeakReference<SQLiteDatabase>(sqliteDatabase));
921921
return sqliteDatabase;
922922
}
923-
923+
924924
public static SQLiteDatabase openOrCreateDatabase(File file, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook){
925925
return openOrCreateDatabase(file.getPath(), password, factory, databaseHook);
926926
}
927927

928928
public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
929929
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook);
930930
}
931-
931+
932932
/**
933933
* Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
934934
*/
@@ -939,7 +939,7 @@ public static SQLiteDatabase openOrCreateDatabase(File file, String password, Cu
939939
/**
940940
* Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
941941
*/
942-
942+
943943
public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory) {
944944
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, null);
945945
}
@@ -968,7 +968,7 @@ public static SQLiteDatabase create(CursorFactory factory, String password) {
968968
* Close the database.
969969
*/
970970
public void close() {
971-
971+
972972
if (!isOpen()) {
973973
return; // already closed
974974
}
@@ -1496,7 +1496,7 @@ public Cursor rawQuery(String sql, String[] selectionArgs,
14961496
SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
14971497
null, sql, selectionArgs, null);
14981498
c.setLoadStyle(initialRead, maxRead);
1499-
1499+
15001500
return c;
15011501
}
15021502

@@ -1875,7 +1875,7 @@ public void rawExecSQL(String sql){
18751875
logTimeStat(sql, timeStart, null);
18761876
}
18771877
}
1878-
1878+
18791879
/**
18801880
* Execute a single SQL statement that is not a query. For example, CREATE
18811881
* TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
@@ -1929,7 +1929,7 @@ protected void finalize() {
19291929
public SQLiteDatabase(String path, String password, CursorFactory factory, int flags) {
19301930
this(path, password, factory, flags, null);
19311931
}
1932-
1932+
19331933
/**
19341934
* Private constructor. See {@link #create} and {@link #openDatabase}.
19351935
*
@@ -1949,12 +1949,12 @@ public SQLiteDatabase(String path, String password, CursorFactory factory, int f
19491949
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
19501950
mFactory = factory;
19511951
dbopen(mPath, mFlags);
1952-
1952+
19531953
if(databaseHook != null){
19541954
databaseHook.preKey(this);
19551955
}
1956-
1957-
execSQL("PRAGMA key = '" + password + "'");
1956+
1957+
native_key(password.toCharArray());
19581958

19591959
if(databaseHook != null){
19601960
databaseHook.postKey(this);
@@ -2370,7 +2370,7 @@ private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbO
23702370
* Sets the root directory to search for the ICU data file
23712371
*/
23722372
public static native void setICURoot(String path);
2373-
2373+
23742374
/**
23752375
* Native call to open the database.
23762376
*
@@ -2435,4 +2435,7 @@ private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbO
24352435
private native void native_rawExecSQL(String sql);
24362436

24372437
private native int native_status(int operation, boolean reset);
2438+
2439+
private native void native_key(char[] key) throws SQLException;
2440+
private native void native_key(String key) throws SQLException;
24382441
}

0 commit comments

Comments
 (0)