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