Bug 718760 - Crypto for the java passwords provider. r=blassey,bsmith

This commit is contained in:
Wes Johnston 2012-03-08 10:25:44 -08:00
parent 3abd6833d2
commit 91d4fd947c
20 changed files with 770 additions and 63 deletions

View File

@ -115,6 +115,7 @@ public class GeckoAppShell
public static native void removeObserver(String observerKey);
public static native void loadGeckoLibsNative(String apkName);
public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
public static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
public static native void onChangeNetworkLinkStatus(String status);
public static native void reportJavaCrash(String stack);
@ -398,6 +399,7 @@ public class GeckoAppShell
}
}
loadSQLiteLibsNative(apkName, extractLibs);
loadNSSLibsNative(apkName, extractLibs);
loadGeckoLibsNative(apkName);
}

View File

@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"/>
#ifdef MOZ_WEBSMS_BACKEND
<!-- WebSMS -->
@ -116,6 +117,13 @@
</intent-filter>
</receiver>
<receiver android:name="org.mozilla.gecko.GeckoMessageReceiver"
android:permission="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER">
<intent-filter>
<action android:name="org.mozilla.gecko.INIT_PW"></action>
</intent-filter>
</receiver>
<activity android:name="Restarter"
android:process="@ANDROID_PACKAGE_NAME@Restarter"
android:theme="@style/Gecko"
@ -175,8 +183,8 @@
<provider android:name="@ANDROID_PACKAGE_NAME@.db.PasswordsProvider"
android:authorities="@ANDROID_PACKAGE_NAME@.db.passwords"
android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"
android:protectionLevel="signature"/>
android:permission="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"
android:process="org.mozilla.gecko.PasswordsProvider"/>
<provider android:name="@ANDROID_PACKAGE_NAME@.db.FormHistoryProvider"
android:authorities="@ANDROID_PACKAGE_NAME@.db.formhistory"
@ -193,6 +201,9 @@
<permission android:name="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"
android:protectionLevel="signature"/>
<permission android:name="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER"
android:protectionLevel="signature"/>
<permission android:name="@ANDROID_PACKAGE_NAME@.permissions.FORMHISTORY_PROVIDER"
android:protectionLevel="signature"/>

View File

@ -109,6 +109,7 @@ abstract public class GeckoApp
public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK";
public static final String ACTION_LOAD = "org.mozilla.gecko.LOAD";
public static final String ACTION_UPDATE = "org.mozilla.gecko.UPDATE";
public static final String ACTION_INIT_PW = "org.mozilla.gecko.INIT_PW";
public static final String SAVED_STATE_URI = "uri";
public static final String SAVED_STATE_TITLE = "title";
public static final String SAVED_STATE_VIEWPORT = "viewport";

View File

@ -112,6 +112,7 @@ public class GeckoAppShell
static File sHomeDir = null;
static private int sDensityDpi = 0;
private static Boolean sSQLiteLibsLoaded = false;
private static Boolean sNSSLibsLoaded = false;
private static Boolean sLibsSetup = false;
private static File sGREDir = null;
@ -141,6 +142,7 @@ public class GeckoAppShell
public static native void removeObserver(String observerKey);
public static native void loadGeckoLibsNative(String apkName);
public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
public static native void loadNSSLibsNative(String apkName, boolean shouldExtract);
public static native void onChangeNetworkLinkStatus(String status);
public static void registerGlobalExceptionHandler() {
@ -371,6 +373,19 @@ public class GeckoAppShell
}
}
public static void loadNSSLibs(Context context, String apkName) {
if (sNSSLibsLoaded)
return;
synchronized(sNSSLibsLoaded) {
if (sNSSLibsLoaded)
return;
loadMozGlue();
loadLibsSetup(context);
loadNSSLibsNative(apkName, false);
sNSSLibsLoaded = true;
}
}
public static void loadMozGlue() {
System.loadLibrary("mozglue");
}

View File

@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class GeckoMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (GeckoApp.ACTION_INIT_PW.equals(action)) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Passwords:Init", null));
}
}
}

View File

@ -90,6 +90,7 @@ public class GeckoThread extends Thread {
String resourcePath = app.getApplication().getPackageResourcePath();
GeckoAppShell.setupGeckoEnvironment(app);
GeckoAppShell.loadSQLiteLibs(app, resourcePath);
GeckoAppShell.loadNSSLibs(app, resourcePath);
GeckoAppShell.loadGeckoLibs(resourcePath);
Locale.setDefault(locale);

View File

@ -88,6 +88,7 @@ FENNEC_JAVA_FILES = \
GeckoEventListener.java \
GeckoEventResponder.java \
GeckoInputConnection.java \
GeckoMessageReceiver.java \
GeckoPreferences.java \
GeckoProfile.java \
GeckoStateListDrawable.java \
@ -95,6 +96,7 @@ FENNEC_JAVA_FILES = \
GlobalHistory.java \
LinkPreference.java \
LinkTextView.java \
NSSBridge.java \
ProfileMigrator.java \
PromptService.java \
sqlite/ByteBufferInputStream.java \

View File

@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.app.Activity;
import android.util.Log;
import android.content.Context;
import java.lang.String;
public class NSSBridge {
private static final String LOGTAG = "NSSBridge";
private static native String nativeEncrypt(String aDb, String aValue);
private static native String nativeDecrypt(String aDb, String aValue);
static public String encrypt(Context context, String aValue) {
String resourcePath = context.getPackageResourcePath();
GeckoAppShell.loadNSSLibs(context, resourcePath);
String res = "";
try {
String path = GeckoProfile.get(context).getDir().toString();
res = nativeEncrypt(path, aValue);
} catch(Exception ex) { }
return res;
}
static public String encrypt(Context context, String profilePath, String aValue) {
String resourcePath = context.getPackageResourcePath();
GeckoAppShell.loadNSSLibs(context, resourcePath);
String res = "";
try {
res = nativeEncrypt(profilePath, aValue);
} catch(Exception ex) { }
return res;
}
static public String decrypt(Context context, String aValue) {
String resourcePath = context.getPackageResourcePath();
GeckoAppShell.loadNSSLibs(context, resourcePath);
String res = "";
try {
String path = GeckoProfile.get(context).getDir().toString();
res = nativeDecrypt(path, aValue);
} catch(Exception ex) { }
return res;
}
static public String decrypt(Context context, String profilePath, String aValue) {
String resourcePath = context.getPackageResourcePath();
GeckoAppShell.loadNSSLibs(context, resourcePath);
String res = "";
try {
res = nativeDecrypt(profilePath, aValue);
} catch(Exception ex) { }
return res;
}
}

View File

@ -45,15 +45,10 @@ public class BrowserContract {
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
public static final String PASSWORDS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.passwords";
public static final String DELETED_PASSWORDS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.deleted-passwords";
public static final Uri PASSWORDS_AUTHORITY_URI = Uri.parse("content://" + PASSWORDS_AUTHORITY);
public static final Uri DELETED_PASSWORDS_AUTHORITY_URI = Uri.parse("content://" + DELETED_PASSWORDS_AUTHORITY);
public static final String FORM_HISTORY_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.formhistory";
public static final String DELETED_FORM_HISTORY_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.deleted-formhistory";
public static final Uri FORM_HISTORY_AUTHORITY_URI = Uri.parse("content://" + FORM_HISTORY_AUTHORITY);
public static final Uri DELETED_FORM_HISTORY_AUTHORITY_URI = Uri.parse("content://" + DELETED_FORM_HISTORY_AUTHORITY);
public static final String TABS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.tabs";
public static final Uri TABS_AUTHORITY_URI = Uri.parse("content://" + TABS_AUTHORITY);
@ -168,7 +163,7 @@ public class BrowserContract {
public static final class DeletedPasswords implements DeletedColumns {
private DeletedPasswords() {}
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/deleted-passwords";
public static final Uri CONTENT_URI = Uri.withAppendedPath(DELETED_PASSWORDS_AUTHORITY_URI, "deleted-formhistory");
public static final Uri CONTENT_URI = Uri.withAppendedPath(PASSWORDS_AUTHORITY_URI, "deleted-passwords");
}
public static final class FormHistory {
@ -187,7 +182,7 @@ public class BrowserContract {
public static final class DeletedFormHistory implements DeletedColumns {
private DeletedFormHistory() {}
public static final Uri CONTENT_URI = Uri.withAppendedPath(DELETED_FORM_HISTORY_AUTHORITY_URI, "deleted-formhistory");
public static final Uri CONTENT_URI = Uri.withAppendedPath(FORM_HISTORY_AUTHORITY_URI, "deleted-formhistory");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/deleted-formhistory";
}

View File

@ -59,7 +59,7 @@ public class DBUtils {
aValues.remove(aOriginalKey);
}
if (!aValues.containsKey(aOriginalKey)) {
if (!aValues.containsKey(aNewKey)) {
aValues.put(aNewKey, value);
}
}

View File

@ -33,9 +33,6 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
@ -60,7 +57,7 @@ public class FormHistoryProvider extends GeckoProvider {
static {
URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(BrowserContract.FORM_HISTORY_AUTHORITY, "formhistory", FORM_HISTORY);
URI_MATCHER.addURI(BrowserContract.DELETED_FORM_HISTORY_AUTHORITY, "deleted-formhistory", DELETED_FORM_HISTORY);
URI_MATCHER.addURI(BrowserContract.FORM_HISTORY_AUTHORITY, "deleted-formhistory", DELETED_FORM_HISTORY);
FORM_HISTORY_PROJECTION_MAP = new HashMap<String, String>();
DELETED_FORM_HISTORY_PROJECTION_MAP = new HashMap<String, String>();
}
@ -146,4 +143,10 @@ public class FormHistoryProvider extends GeckoProvider {
public void initGecko() {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormHistory:Init", null));
}
public void onPreInsert(ContentValues values, Uri uri) { }
public void onPreUpdate(ContentValues values, Uri uri) { }
public void onPostQuery(Cursor cursor, Uri uri) { }
}

View File

@ -90,6 +90,7 @@ public abstract class GeckoProvider extends ContentProvider {
try {
String resourcePath = context.getPackageResourcePath();
GeckoAppShell.loadSQLiteLibs(context, resourcePath);
GeckoAppShell.loadNSSLibs(context, resourcePath);
bridge = new SQLiteBridge(databasePath);
int version = bridge.getVersion();
Log.i(mLogTag, version + " == " + mDBVersion);
@ -221,6 +222,8 @@ public abstract class GeckoProvider extends ContentProvider {
setupDefaults(uri, values);
onPreInsert(values, uri);
try {
id = db.insert(getTable(uri), null, values);
} catch(SQLiteBridgeException ex) {
@ -242,6 +245,8 @@ public abstract class GeckoProvider extends ContentProvider {
if (db == null)
return updated;
onPreUpdate(values, uri);
try {
updated = db.update(getTable(uri), values, selection, selectionArgs);
} catch(SQLiteBridgeException ex) {
@ -267,6 +272,7 @@ public abstract class GeckoProvider extends ContentProvider {
try {
cursor = db.query(getTable(uri), projection, selection, selectionArgs, null, null, sortOrder, null);
onPostQuery(cursor, uri);
} catch (SQLiteBridgeException ex) {
Log.e(mLogTag, "Error querying database", ex);
}
@ -281,4 +287,10 @@ public abstract class GeckoProvider extends ContentProvider {
public abstract void setupDefaults(Uri uri, ContentValues values);
public abstract void initGecko();
public abstract void onPreInsert(ContentValues values, Uri uri);
public abstract void onPreUpdate(ContentValues values, Uri uri);
public abstract void onPostQuery(Cursor cursor, Uri uri);
}

View File

@ -17,12 +17,13 @@ import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventListener;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.db.BrowserContract.CommonColumns;
import org.mozilla.gecko.NSSBridge;
import org.mozilla.gecko.db.DBUtils;
import org.mozilla.gecko.db.BrowserContract.Passwords;
import org.mozilla.gecko.db.BrowserContract.DeletedPasswords;
import org.mozilla.gecko.db.BrowserContract.SyncColumns;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sqlite.MatrixBlobCursor;
import org.mozilla.gecko.sqlite.SQLiteBridge;
import org.mozilla.gecko.sqlite.SQLiteBridgeException;
import org.mozilla.gecko.sync.Utils;
@ -31,11 +32,9 @@ import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
@ -82,12 +81,13 @@ public class PasswordsProvider extends GeckoProvider {
PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_PASSWORD_CHANGED, Passwords.TIME_PASSWORD_CHANGED);
PASSWORDS_PROJECTION_MAP.put(Passwords.TIMES_USED, Passwords.TIMES_USED);
URI_MATCHER.addURI(BrowserContract.DELETED_PASSWORDS_AUTHORITY, "deleted-passwords", DELETED_PASSWORDS);
URI_MATCHER.addURI(BrowserContract.PASSWORDS_AUTHORITY, "deleted-passwords", DELETED_PASSWORDS);
DELETED_PASSWORDS_PROJECTION_MAP = new HashMap<String, String>();
DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.ID, DeletedPasswords.ID);
DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.GUID, DeletedPasswords.GUID);
DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.TIME_DELETED, DeletedPasswords.TIME_DELETED);
System.loadLibrary("mozglue");
}
@Override
@ -189,5 +189,78 @@ public class PasswordsProvider extends GeckoProvider {
@Override
public void initGecko() {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Passwords:Init", null));
Intent initIntent = new Intent(GeckoApp.ACTION_INIT_PW);
mContext.sendBroadcast(initIntent);
}
private String doCrypto(String initialValue, Uri uri, Boolean encrypt) {
String profilePath = null;
if (uri != null) {
profilePath = uri.getQueryParameter(BrowserContract.PARAM_PROFILE_PATH);
}
String result = "";
if (encrypt) {
if (profilePath != null) result = NSSBridge.encrypt(mContext, profilePath, initialValue);
else result = NSSBridge.encrypt(mContext, initialValue);
} else {
if (profilePath != null) result = NSSBridge.decrypt(mContext, profilePath, initialValue);
else result = NSSBridge.decrypt(mContext, initialValue);
}
return result;
}
public void onPreInsert(ContentValues values, Uri uri) {
if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) {
String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_PASSWORD), uri, true);
values.put(Passwords.ENCRYPTED_PASSWORD, res);
}
if (values.containsKey(Passwords.ENCRYPTED_USERNAME)) {
String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_USERNAME), uri, true);
values.put(Passwords.ENCRYPTED_USERNAME, res);
}
}
public void onPreUpdate(ContentValues values, Uri uri) {
if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) {
String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_PASSWORD), uri, true);
values.put(Passwords.ENCRYPTED_PASSWORD, res);
}
if (values.containsKey(Passwords.ENCRYPTED_USERNAME)) {
String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_USERNAME), uri, true);
values.put(Passwords.ENCRYPTED_USERNAME, res);
}
}
public void onPostQuery(Cursor cursor, Uri uri) {
int passwordIndex = -1;
int usernameIndex = -1;
String profilePath = null;
try {
passwordIndex = cursor.getColumnIndexOrThrow(Passwords.ENCRYPTED_PASSWORD);
} catch(Exception ex) { }
try {
usernameIndex = cursor.getColumnIndexOrThrow(Passwords.ENCRYPTED_USERNAME);
} catch(Exception ex) { }
if (passwordIndex > -1 || usernameIndex > -1) {
MatrixBlobCursor m = (MatrixBlobCursor)cursor;
if (cursor.moveToFirst()) {
do {
if (passwordIndex > -1) {
String decrypted = doCrypto(cursor.getString(passwordIndex), uri, false);;
m.set(passwordIndex, decrypted);
}
if (usernameIndex > -1) {
String decrypted = doCrypto(cursor.getString(usernameIndex), uri, false);
m.set(usernameIndex, decrypted);
}
} while(cursor.moveToNext());
}
}
}
}

View File

@ -228,8 +228,21 @@ public class MatrixBlobCursor extends AbstractCursor {
}
}
// AbstractCursor implementation.
public void set(int column, Object value) {
if (column < 0 || column >= columnCount) {
throw new CursorIndexOutOfBoundsException("Requested column: "
+ column + ", # of columns: " + columnCount);
}
if (mPos < 0) {
throw new CursorIndexOutOfBoundsException("Before first row.");
}
if (mPos >= rowCount) {
throw new CursorIndexOutOfBoundsException("After last row.");
}
data[mPos * columnCount + column] = value;
}
// AbstractCursor implementation.
@Override
public int getCount() {
return rowCount;

View File

@ -66,6 +66,7 @@
#include "Zip.h"
#include "sqlite3.h"
#include "SQLiteBridge.h"
#include "NSSBridge.h"
#ifndef MOZ_OLD_LINKER
#include "ElfLoader.h"
#endif
@ -76,6 +77,8 @@
#define RUSAGE_THREAD 1
#endif
typedef int mozglueresult;
enum StartupEvent {
#define mozilla_StartupTimeline_Event(ev, z) ev,
#include "StartupTimeline.h"
@ -99,6 +102,23 @@ getLibraryMapping()
return lib_mapping;
}
void
JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg)
{
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n");
jclass cls = jenv->FindClass(classname);
if (cls == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname);
exit(FAILURE);
}
int rc = jenv->ThrowNew(cls, msg);
if (rc < 0) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg);
exit(FAILURE);
}
jenv->DeleteLocalRef(cls);
}
#ifdef MOZ_OLD_LINKER
static int
createAshmem(size_t bytes, const char *name)
@ -313,6 +333,11 @@ SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong)
static void * xul_handle = NULL;
static void * sqlite_handle = NULL;
static void * nss_handle = NULL;
static void * nspr_handle = NULL;
static void * plc_handle = NULL;
static bool simple_linker_initialized = false;
#ifdef MOZ_OLD_LINKER
static time_t apk_mtime = 0;
#ifdef DEBUG
@ -641,7 +666,7 @@ report_mapping(char *name, void *base, uint32_t len, uint32_t offset)
extern "C" void simple_linker_init(void);
#endif
static void
static mozglueresult
loadGeckoLibs(const char *apkName)
{
chdir(getenv("GRE_HOME"));
@ -673,18 +698,8 @@ loadGeckoLibs(const char *apkName)
#else
#define MOZLOAD(name) mozload("lib" name ".so", zip)
MOZLOAD("mozalloc");
MOZLOAD("nspr4");
MOZLOAD("plc4");
MOZLOAD("plds4");
MOZLOAD("nssutil3");
MOZLOAD("nss3");
MOZLOAD("ssl3");
MOZLOAD("smime3");
xul_handle = MOZLOAD("xul");
MOZLOAD("xpcom");
MOZLOAD("nssckbi");
MOZLOAD("freebl3");
MOZLOAD("softokn3");
#undef MOZLOAD
#endif
@ -693,8 +708,10 @@ loadGeckoLibs(const char *apkName)
file_ids = NULL;
#endif
if (!xul_handle)
if (!xul_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!");
return FAILURE;
}
#define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(xul_handle, "Java_org_mozilla_gecko_GeckoAppShell_" #name)
GETFUNC(nativeInit);
@ -738,14 +755,18 @@ loadGeckoLibs(const char *apkName)
StartupTimeline_Record(LINKER_INITIALIZED, &t0);
StartupTimeline_Record(LIBRARIES_LOADED, &t1);
return SUCCESS;
}
static void loadSQLiteLibs(const char *apkName)
static int loadSQLiteLibs(const char *apkName)
{
chdir(getenv("GRE_HOME"));
#ifdef MOZ_OLD_LINKER
simple_linker_init();
if (!simple_linker_initialized) {
simple_linker_init();
simple_linker_initialized = true;
}
struct stat status;
if (!stat(apkName, &status))
@ -753,7 +774,9 @@ static void loadSQLiteLibs(const char *apkName)
#endif
RefPtr<Zip> zip = new Zip(apkName);
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
if (!lib_mapping) {
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
}
#ifdef MOZ_CRASHREPORTER
file_ids = (char *)extractBuf("lib.id", zip);
@ -775,10 +798,96 @@ static void loadSQLiteLibs(const char *apkName)
file_ids = NULL;
#endif
if (!sqlite_handle)
if (!sqlite_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!");
return FAILURE;
}
setup_sqlite_functions(sqlite_handle);
return SUCCESS;
}
static mozglueresult
loadNSSLibs(const char *apkName)
{
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs");
chdir(getenv("GRE_HOME"));
#ifdef MOZ_OLD_LINKER
if (!simple_linker_initialized) {
simple_linker_init();
simple_linker_initialized = true;
}
struct stat status;
if (!stat(apkName, &status))
apk_mtime = status.st_mtime;
#endif
Zip *zip = new Zip(apkName);
if (!lib_mapping) {
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
}
#ifdef MOZ_CRASHREPORTER
file_ids = (char *)extractBuf("lib.id", zip);
#endif
#ifndef MOZ_OLD_LINKER
char *file = new char[strlen(apkName) + sizeof("!/libnss3.so")];
sprintf(file, "%s!/libnss3.so", apkName);
nss_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
file = new char[strlen(apkName) + sizeof("!/libnspr4.so")];
sprintf(file, "%s!/libnspr4.so", apkName);
nspr_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
file = new char[strlen(apkName) + sizeof("!/libplc4.so")];
sprintf(file, "%s!/libplc4.so", apkName);
plc_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY);
delete [] file;
#else
#define MOZLOAD(name) mozload("lib" name ".so", zip)
nspr_handle = MOZLOAD("nspr4");
plc_handle = MOZLOAD("plc4");
MOZLOAD("plds4");
MOZLOAD("nssutil3");
nss_handle = MOZLOAD("nss3");
MOZLOAD("ssl3");
MOZLOAD("smime3");
MOZLOAD("nssckbi");
MOZLOAD("freebl3");
MOZLOAD("softokn3");
#undef MOZLOAD
#endif
delete zip;
#ifdef MOZ_CRASHREPORTER
free(file_ids);
file_ids = NULL;
#endif
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs 2");
if (!nss_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!");
return FAILURE;
}
if (!nspr_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!");
return FAILURE;
}
if (!plc_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!");
return FAILURE;
}
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "loadNSSLibs 3");
return setup_nss_functions(nss_handle, nspr_handle, plc_handle);
}
extern "C" NS_EXPORT void JNICALL
@ -791,7 +900,10 @@ Java_org_mozilla_gecko_GeckoAppShell_loadGeckoLibsNative(JNIEnv *jenv, jclass jG
if (str == NULL)
return;
loadGeckoLibs(str);
int res = loadGeckoLibs(str);
if (res != SUCCESS) {
JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries");
}
jenv->ReleaseStringUTFChars(jApkName, str);
}
@ -812,7 +924,38 @@ Java_org_mozilla_gecko_GeckoAppShell_loadSQLiteLibsNative(JNIEnv *jenv, jclass j
if (str == NULL)
return;
loadSQLiteLibs(str);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n");
mozglueresult rv = loadSQLiteLibs(str);
if (rv != SUCCESS) {
JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries");
}
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n");
jenv->ReleaseStringUTFChars(jApkName, str);
}
extern "C" NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) {
if (jShouldExtract) {
#ifdef MOZ_OLD_LINKER
extractLibs = 1;
#else
putenv("MOZ_LINKER_EXTRACT=1");
#endif
}
const char* str;
// XXX: java doesn't give us true UTF8, we should figure out something
// better to do here
str = jenv->GetStringUTFChars(jApkName, NULL);
if (str == NULL)
return;
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n");
mozglueresult rv = loadNSSLibs(str);
if (rv != SUCCESS) {
JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries");
}
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n");
jenv->ReleaseStringUTFChars(jApkName, str);
}
@ -836,9 +979,8 @@ Java_org_mozilla_gecko_GeckoAppShell_nativeRun(JNIEnv *jenv, jclass jc, jstring
}
typedef int GeckoProcessType;
typedef int nsresult;
extern "C" NS_EXPORT int
extern "C" NS_EXPORT mozglueresult
ChildProcessInit(int argc, char* argv[])
{
int i;
@ -853,14 +995,21 @@ ChildProcessInit(int argc, char* argv[])
#ifdef MOZ_OLD_LINKER
fillLibCache(argv[argc - 1]);
#endif
loadSQLiteLibs(argv[i]);
loadGeckoLibs(argv[i]);
if (loadNSSLibs(argv[i]) != SUCCESS) {
return FAILURE;
}
if (loadSQLiteLibs(argv[i]) != SUCCESS) {
return FAILURE;
}
if (loadGeckoLibs(argv[i]) != SUCCESS) {
return FAILURE;
}
// don't pass the last arg - it's only recognized by the lib cache
argc--;
typedef GeckoProcessType (*XRE_StringToChildProcessType_t)(char*);
typedef nsresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType);
typedef mozglueresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType);
XRE_StringToChildProcessType_t fXRE_StringToChildProcessType =
(XRE_StringToChildProcessType_t)__wrap_dlsym(xul_handle, "XRE_StringToChildProcessType");
XRE_InitChildProcess_t fXRE_InitChildProcess =
@ -868,10 +1017,6 @@ ChildProcessInit(int argc, char* argv[])
GeckoProcessType proctype = fXRE_StringToChildProcessType(argv[--argc]);
nsresult rv = fXRE_InitChildProcess(argc, argv, proctype);
if (rv != 0)
return 1;
return 0;
return fXRE_InitChildProcess(argc, argv, proctype);
}

View File

@ -37,6 +37,8 @@
#ifndef APKOpen_h
#define APKOpen_h
#include <jni.h>
#ifndef NS_EXPORT
#define NS_EXPORT __attribute__ ((visibility("default")))
#endif
@ -51,6 +53,10 @@ struct mapping_info {
const struct mapping_info * getLibraryMapping();
static const int SUCCESS = 0;
static const int FAILURE = 1;
void JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg);
#define MAX_LIB_CACHE_ENTRIES 32
#define MAX_LIB_CACHE_NAME_LEN 32
@ -63,4 +69,5 @@ struct lib_cache_info {
NS_EXPORT const struct lib_cache_info * getLibraryCache();
#endif /* APKOpen_h */

View File

@ -55,6 +55,7 @@ CPPSRCS = \
nsGeckoUtils.cpp \
APKOpen.cpp \
SQLiteBridge.cpp \
NSSBridge.cpp \
$(NULL)
LOCAL_INCLUDES += -I$(DEPTH)/build
@ -63,6 +64,28 @@ LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
LOCAL_INCLUDES += -I$(srcdir)/../linker
LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/startup
LOCAL_INCLUDES += -I$(topsrcdir)/db/sqlite3/src
LOCAL_INCLUDES += -I$(topsrcdir)/base/src
LOCAL_INCLUDES += -I$(topsrcdir)/nsprpub/lib/ds
LOCAL_INCLUDES += -I$(topsrcdir)/nsprpub/lib/libc/include
LOCAL_INCLUDES += -I$(topsrcdir)/nsprpub/pr/include
LOCAL_INCLUDES += -I$(topsrcdir)/ipc/chromium/src/base/third_party/nspr
LOCAL_INCLUDES += -I$(topsrcdir)/ipc/chromium/src
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/nss
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/util
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/softoken
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/pk11wrap
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/cmd/ecperf
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/pkcs7
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/certdb
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/cryptohi
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/dev
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/base
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/pki
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/smime
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/freebl/
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/lib/ssl
LOCAL_INCLUDES += -I$(topsrcdir)/security/nss/cmd/lib/
ifdef MOZ_OLD_LINKER
DEFINES += -DMOZ_OLD_LINKER
LOCAL_INCLUDES += -I$(topsrcdir)/other-licenses/android

View File

@ -0,0 +1,300 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdlib.h>
#include "dlfcn.h"
#include "NSSBridge.h"
#include "APKOpen.h"
#ifdef ANDROID
#include <jni.h>
#include <android/log.h>
#endif
#ifndef MOZ_OLD_LINKER
#include "ElfLoader.h"
#endif
#define DEBUG 1
#ifdef DEBUG
#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
#else
#define LOG(x...) printf(x);
#endif
static bool initialized = false;
#define NSS_WRAPPER_INT(name) name ## _t f_ ## name;
NSS_WRAPPER_INT(NSS_Initialize)
NSS_WRAPPER_INT(NSS_Shutdown)
NSS_WRAPPER_INT(SECITEM_ZfreeItem)
NSS_WRAPPER_INT(PK11SDR_Encrypt)
NSS_WRAPPER_INT(PK11SDR_Decrypt)
NSS_WRAPPER_INT(PK11_GetInternalKeySlot)
NSS_WRAPPER_INT(PK11_NeedUserInit)
NSS_WRAPPER_INT(PK11_InitPin)
NSS_WRAPPER_INT(PR_ErrorToString)
NSS_WRAPPER_INT(PR_GetError)
NSS_WRAPPER_INT(PR_Free)
NSS_WRAPPER_INT(PL_Base64Encode)
NSS_WRAPPER_INT(PL_Base64Decode)
NSS_WRAPPER_INT(PL_strfree)
int
setup_nss_functions(void *nss_handle,
void *nspr_handle,
void *plc_handle)
{
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "setup nss 1");
if (nss_handle == NULL || nspr_handle == NULL || plc_handle == NULL) {
LOG("missing handle\n");
return FAILURE;
}
#define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(nss_handle, #name); \
if (!f_ ##name) return FAILURE;
GETFUNC(NSS_Initialize);
GETFUNC(NSS_Shutdown);
GETFUNC(PK11SDR_Encrypt);
GETFUNC(PK11SDR_Decrypt);
GETFUNC(PK11_GetInternalKeySlot);
GETFUNC(PK11_NeedUserInit);
GETFUNC(PK11_InitPin);
GETFUNC(SECITEM_ZfreeItem);
#undef GETFUNC
#define NSPRFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(nspr_handle, #name); \
if (!f_ ##name) return FAILURE;
NSPRFUNC(PR_ErrorToString);
NSPRFUNC(PR_GetError);
NSPRFUNC(PR_Free);
#undef NSPRFUNC
#define PLCFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(plc_handle, #name); \
if (!f_ ##name) return FAILURE;
PLCFUNC(PL_Base64Encode);
PLCFUNC(PL_Base64Decode);
PLCFUNC(PL_strfree);
#undef PLCFUNC
return SUCCESS;
}
/* Throws the current NSS error. */
static void
throwError(JNIEnv* jenv, const char * funcString) {
char *msg;
PRErrorCode perr = f_PR_GetError();
char * errString = f_PR_ErrorToString(perr, 0);
asprintf(&msg, "%s returned error %d: %s\n", funcString, perr, errString);
LOG("Throwing error: %s\n", msg);
JNI_Throw(jenv, "java/lang/Exception", msg);
free(msg);
LOG("Error thrown\n");
}
extern "C" NS_EXPORT jstring JNICALL
Java_org_mozilla_gecko_NSSBridge_nativeEncrypt(JNIEnv* jenv, jclass,
jstring jPath,
jstring jValue)
{
jstring ret = jenv->NewStringUTF("");
const char* path;
path = jenv->GetStringUTFChars(jPath, NULL);
const char* value;
value = jenv->GetStringUTFChars(jValue, NULL);
char* result;
SECStatus rv = doCrypto(jenv, path, value, &result, true);
if (rv == SECSuccess) {
ret = jenv->NewStringUTF(result);
free(result);
}
jenv->ReleaseStringUTFChars(jValue, value);
jenv->ReleaseStringUTFChars(jPath, path);
return ret;
}
extern "C" NS_EXPORT jstring JNICALL
Java_org_mozilla_gecko_NSSBridge_nativeDecrypt(JNIEnv* jenv, jclass,
jstring jPath,
jstring jValue)
{
jstring ret = jenv->NewStringUTF("");
const char* path;
path = jenv->GetStringUTFChars(jPath, NULL);
const char* value;
value = jenv->GetStringUTFChars(jValue, NULL);
char* result;
SECStatus rv = doCrypto(jenv, path, value, &result, false);
if (rv == SECSuccess) {
ret = jenv->NewStringUTF(result);
free(result);
}
jenv->ReleaseStringUTFChars(jValue, value);
jenv->ReleaseStringUTFChars(jPath, path);
return ret;
}
/* Encrypts or decrypts a string. result should be freed with free() when done */
SECStatus
doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool encrypt)
{
SECStatus rv;
PK11SlotInfo *slot;
if (!initialized) {
LOG("initialize crypto %s\n", path);
rv = f_NSS_Initialize(path, "", "", "secmod.db", NSS_INIT_NOROOTINIT);
if (rv != SECSuccess) {
throwError(jenv, "NSS_Initialize");
return rv;
}
initialized = true;
}
slot = f_PK11_GetInternalKeySlot();
if (!slot) {
throwError(jenv, "PK11_GetInternalKeySlot");
return SECFailure;
}
if (f_PK11_NeedUserInit(slot)) {
LOG("Initializing key3.db with default blank password.");
rv = f_PK11_InitPin(slot, NULL, NULL);
if (rv != SECSuccess) {
throwError(jenv, "PK11_InitPin");
return rv;
}
}
SECItem request;
SECItem reply;
reply.data = 0;
reply.len = 0;
if (encrypt) {
LOG("encrypting %s\n", value);
request.data = (unsigned char*)value;
request.len = strlen(value);
SECItem keyid;
keyid.data = 0;
keyid.len = 0;
rv = f_PK11SDR_Encrypt(&keyid, &request, &reply, NULL);
if (rv != SECSuccess) {
throwError(jenv, "PK11SDR_Encrypt");
goto done;
}
rv = encode(reply.data, reply.len, result);
if (rv != SECSuccess) {
throwError(jenv, "encode");
goto done;
}
LOG("encrypted %s\n", *result);
} else {
LOG("decoding %s\n", value);
rv = decode(value, &request.data, (PRInt32*)&request.len);
if (rv != SECSuccess) {
throwError(jenv, "decode");
return rv;
}
rv = f_PK11SDR_Decrypt(&request, &reply, NULL);
if (rv != SECSuccess) {
throwError(jenv, "PK11SDR_Decrypt");
goto done;
}
*result = (char *)malloc(reply.len);
(*result)[reply.len] = '\0';
strncpy(*result, (char *)reply.data, reply.len);
//asprintf(result, "%s", (char *)reply.data);
LOG("decoded %i letters %s\n", reply.len, *result);
free(request.data);
}
done:
f_SECITEM_ZfreeItem(&reply, false);
return rv;
}
/*
* Base64 encodes the data passed in. The caller must deallocate _retval using free();
*/
SECStatus
encode(const unsigned char *data, PRInt32 dataLen, char **_retval)
{
SECStatus rv = SECSuccess;
char *encoded = f_PL_Base64Encode((const char *)data, dataLen, NULL);
if (!encoded)
rv = SECFailure;
if (!*encoded)
rv = SECFailure;
if (rv == SECSuccess) {
*_retval = (char *)malloc(strlen(encoded));
strcpy(*_retval, encoded);
}
if (encoded) {
f_PR_Free(encoded);
}
return rv;
}
/*
* Base64 decodes the data passed in. The caller must deallocate result using free();
*/
SECStatus
decode(const char *data, unsigned char **result, PRInt32 *length)
{
SECStatus rv = SECSuccess;
PRUint32 len = strlen(data);
int adjust = 0;
/* Compute length adjustment */
if (len > 0 && data[len-1] == '=') {
adjust++;
if (data[len-2] == '=') adjust++;
}
char *decoded;
decoded = f_PL_Base64Decode(data, len, NULL);
if (!decoded) {
return SECFailure;
}
LOG("xxx Decoded: %s\n", decoded);
if (!*decoded) {
return SECFailure;
}
*length = (len*3)/4 - adjust;
*result = (unsigned char*)malloc((size_t)len);
if (!*result) {
rv = SECFailure;
} else {
memcpy((char*)*result, decoded, len);
}
f_PR_Free(decoded);
return rv;
}

View File

@ -0,0 +1,36 @@
#ifndef NSSBridge_h
#define NSSBridge_h
#include "nss.h"
#include "seccomon.h"
#include "secmodt.h"
#include "secutil.h"
#include "pk11func.h"
#include <jni.h>
int setup_nss_functions(void *nss_handle, void *nssutil_handle, void *plc_handle);
#define NSS_WRAPPER(name, return_type, args...) \
typedef return_type (*name ## _t)(args); \
extern name ## _t f_ ## name;
NSS_WRAPPER(NSS_Initialize, SECStatus, const char*, const char*, const char*, const char*, PRUint32)
NSS_WRAPPER(NSS_Shutdown, void, void)
NSS_WRAPPER(PK11SDR_Encrypt, SECStatus, SECItem *, SECItem *, SECItem *, void *)
NSS_WRAPPER(PK11SDR_Decrypt, SECStatus, SECItem *, SECItem *, void *)
NSS_WRAPPER(SECITEM_ZfreeItem, void, SECItem*, PRBool)
NSS_WRAPPER(PR_ErrorToString, char *, PRErrorCode, PRLanguageCode)
NSS_WRAPPER(PR_GetError, PRErrorCode, void)
NSS_WRAPPER(PR_Free, PRErrorCode, char *)
NSS_WRAPPER(PL_Base64Encode, char*, const char*, PRUint32, char*)
NSS_WRAPPER(PL_Base64Decode, char*, const char*, PRUint32, char*)
NSS_WRAPPER(PL_strfree, void, char*)
NSS_WRAPPER(PK11_GetInternalKeySlot, PK11SlotInfo *, void)
NSS_WRAPPER(PK11_NeedUserInit, PRBool, PK11SlotInfo *)
NSS_WRAPPER(PK11_InitPin, SECStatus, PK11SlotInfo*, const char*, const char*)
bool setPassword(PK11SlotInfo *slot);
SECStatus doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool doEncrypt);
SECStatus encode(const unsigned char *data, PRInt32 dataLen, char **_retval);
SECStatus decode(const char *data, unsigned char **result, PRInt32 * _retval);
#endif /* NSS_h */

View File

@ -101,21 +101,6 @@ static jmethodID jByteBufferAllocateDirect;
static jmethodID jCursorConstructor;
static jmethodID jCursorAddRow;
static void
JNI_Throw(JNIEnv* jenv, const char* name, const char* msg)
{
jclass cls = jenv->FindClass(name);
if (cls == NULL) {
LOG("Couldn't find exception class (or exception pending)\n");
return;
}
int rc = jenv->ThrowNew(cls, msg);
if (rc < 0) {
LOG("Error throwing exception\n");
}
jenv->DeleteLocalRef(cls);
}
static void
JNI_Setup(JNIEnv* jenv)
{