Add SameSignatureSecurityPolicy for binder transport (#27816)

Tested signing example server and example client APKs with different
debug key, worked as intended.
pull/28598/head
Ming-Chuan 3 years ago committed by GitHub
parent 6703186b7a
commit 93733de253
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      examples/android/binder/java/io/grpc/binder/cpp/exampleclient/native.cc
  2. 5
      examples/android/binder/java/io/grpc/binder/cpp/exampleserver/ExportedEndpointService.java
  3. 23
      examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc
  4. 24
      include/grpcpp/security/binder_security_policy.h
  5. 18
      src/core/ext/transport/binder/client/jni_utils.cc
  6. 4
      src/core/ext/transport/binder/client/jni_utils.h
  7. 15
      src/core/ext/transport/binder/java/io/grpc/binder/cpp/NativeConnectionHelper.java
  8. 53
      src/core/ext/transport/binder/security_policy/binder_security_policy.cc

@ -24,6 +24,9 @@
extern "C" JNIEXPORT jstring JNICALL
Java_io_grpc_binder_cpp_exampleclient_ButtonPressHandler_native_1entry(
JNIEnv* env, jobject /*this*/, jobject application) {
// Lower the gRPC logging level, here it is just for demo and debugging
// purpose.
setenv("GRPC_VERBOSITY", "INFO", true);
if (grpc::experimental::InitializeBinderChannelJavaClass(env)) {
__android_log_print(ANDROID_LOG_INFO, "DemoClient",
"InitializeBinderChannelJavaClass succeed");
@ -35,12 +38,17 @@ Java_io_grpc_binder_cpp_exampleclient_ButtonPressHandler_native_1entry(
static std::shared_ptr<grpc::Channel> channel;
if (first) {
first = false;
// TODO(mingcl): Use same signature security after it become available
JavaVM* jvm;
{
jint result = env->GetJavaVM(&jvm);
assert(result == 0);
}
channel = grpc::experimental::CreateBinderChannel(
env, application, "io.grpc.binder.cpp.exampleserver",
"io.grpc.binder.cpp.exampleserver.ExportedEndpointService",
std::make_shared<
grpc::experimental::binder::UntrustedSecurityPolicy>());
grpc::experimental::binder::SameSignatureSecurityPolicy>(
jvm, application));
return env->NewStringUTF("Clicked 1 time, channel created");
} else {
auto stub = helloworld::Greeter::NewStub(channel);

@ -3,6 +3,7 @@ package io.grpc.binder.cpp.exampleserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.content.Context;
import io.grpc.binder.cpp.GrpcCppServerBuilder;
/** Exposes gRPC services running in the main process */
@ -12,7 +13,7 @@ public final class ExportedEndpointService extends Service {
}
public ExportedEndpointService() {
init_grpc_server();
init_grpc_server(this);
}
@Override
@ -21,5 +22,5 @@ public final class ExportedEndpointService extends Service {
return GrpcCppServerBuilder.GetEndpointBinder("binder:example.service");
}
public native void init_grpc_server();
public native void init_grpc_server(Context context);
}

@ -18,6 +18,7 @@
#include "examples/protos/helloworld.grpc.pb.h"
#include "examples/protos/helloworld.pb.h"
#include <grpcpp/create_channel_binder.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/binder_credentials.h>
#include <grpcpp/security/binder_security_policy.h>
@ -41,7 +42,10 @@ class GreeterService : public helloworld::Greeter::Service {
extern "C" JNIEXPORT void JNICALL
Java_io_grpc_binder_cpp_exampleserver_ExportedEndpointService_init_1grpc_1server(
JNIEnv* env, jobject /*this*/) {
JNIEnv* env, jobject /*this*/, jobject context) {
// Lower the gRPC logging level, here it is just for demo and debugging
// purpose.
setenv("GRPC_VERBOSITY", "INFO", true);
__android_log_print(ANDROID_LOG_INFO, "DemoServer", "Line number %d",
__LINE__);
static std::unique_ptr<grpc::Server> server = nullptr;
@ -51,16 +55,29 @@ Java_io_grpc_binder_cpp_exampleserver_ExportedEndpointService_init_1grpc_1server
return;
}
if (grpc::experimental::InitializeBinderChannelJavaClass(env)) {
__android_log_print(ANDROID_LOG_INFO, "DemoServer",
"InitializeBinderChannelJavaClass succeed");
} else {
__android_log_print(ANDROID_LOG_INFO, "DemoServer",
"InitializeBinderChannelJavaClass failed");
}
static GreeterService service;
grpc::ServerBuilder server_builder;
server_builder.RegisterService(&service);
// TODO(mingcl): Use same signature security after it become available
JavaVM* jvm;
{
jint result = env->GetJavaVM(&jvm);
assert(result == 0);
}
server_builder.AddListeningPort(
"binder:example.service",
grpc::experimental::BinderServerCredentials(
std::make_shared<
grpc::experimental::binder::UntrustedSecurityPolicy>()));
grpc::experimental::binder::SameSignatureSecurityPolicy>(
jvm, context)));
server = server_builder.BuildAndStart();
}

@ -17,6 +17,12 @@
#include <memory>
#ifdef GPR_ANDROID
#include <jni.h>
#endif
namespace grpc {
namespace experimental {
namespace binder {
@ -51,6 +57,24 @@ class InternalOnlySecurityPolicy : public SecurityPolicy {
bool IsAuthorized(int uid) override;
};
#ifdef GPR_ANDROID
// EXPERIMENTAL Only allows the connections from the APK that have the same
// signature.
class SameSignatureSecurityPolicy : public SecurityPolicy {
public:
// `context` is required for getting PackageManager Java class
SameSignatureSecurityPolicy(JavaVM* jvm, jobject context);
~SameSignatureSecurityPolicy() override;
bool IsAuthorized(int uid) override;
private:
JavaVM* jvm_;
jobject context_;
};
#endif
} // namespace binder
} // namespace experimental
} // namespace grpc

@ -87,6 +87,24 @@ void TryEstablishConnection(JNIEnv* env, jobject application,
env->NewStringUTF(std::string(conn_id).c_str()));
}
bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2) {
const std::string method = "isSignatureMatch";
const std::string type = "(Landroid/content/Context;II)Z";
jclass cl = FindNativeConnectionHelper(env);
if (cl == nullptr) {
return false;
}
jmethodID mid = env->GetStaticMethodID(cl, method.c_str(), type.c_str());
if (mid == nullptr) {
gpr_log(GPR_ERROR, "No method id %s", method.c_str());
}
jboolean result = env->CallStaticBooleanMethod(cl, mid, context, uid1, uid2);
return result == JNI_TRUE;
}
} // namespace grpc_binder
#endif

@ -42,6 +42,10 @@ void TryEstablishConnection(JNIEnv* env, jobject application,
absl::string_view pkg, absl::string_view cls,
absl::string_view conn_id);
// Calls Java method NativeConnectionHelper.isSignatureMatch.
// Will also return false if failed to invoke Java.
bool IsSignatureMatch(JNIEnv* env, jobject context, int uid1, int uid2);
} // namespace grpc_binder
#endif

@ -15,7 +15,9 @@
package io.grpc.binder.cpp;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Parcel;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
@ -34,6 +36,19 @@ final class NativeConnectionHelper {
s.get(connId).tryConnect(pkg, cls);
}
// Returns true if the packages signature of the 2 UIDs match.
// `context` is used to get PackageManager
static boolean isSignatureMatch(Context context, int uid1, int uid2) {
int result = context.getPackageManager().checkSignatures(uid1, uid2);
if (result == PackageManager.SIGNATURE_MATCH) {
return true;
}
Log.e(
"NativeConnectionHelper",
"Signatures does not match. checkSignature return value = " + result);
return false;
}
static Parcel getEmptyParcel() {
return Parcel.obtain();
}

@ -20,8 +20,13 @@
#ifdef GPR_ANDROID
#include <jni.h>
#include <unistd.h>
#include <grpc/support/log.h>
#include "src/core/ext/transport/binder/client/jni_utils.h"
#endif
namespace grpc {
@ -46,6 +51,54 @@ bool InternalOnlySecurityPolicy::IsAuthorized(int uid) {
bool InternalOnlySecurityPolicy::IsAuthorized(int) { return false; }
#endif
#ifdef GPR_ANDROID
namespace {
JNIEnv* GetEnv(JavaVM* vm) {
if (vm == nullptr) return nullptr;
JNIEnv* result = nullptr;
jint attach = vm->AttachCurrentThread(&result, nullptr);
GPR_ASSERT(JNI_OK == attach);
GPR_ASSERT(nullptr != result);
return result;
}
} // namespace
SameSignatureSecurityPolicy::SameSignatureSecurityPolicy(JavaVM* jvm,
jobject context)
: jvm_(jvm) {
GPR_ASSERT(jvm != nullptr);
GPR_ASSERT(context != nullptr);
JNIEnv* env = GetEnv(jvm_);
// Make sure the context is still valid when IsAuthorized() is called
context_ = env->NewGlobalRef(context);
GPR_ASSERT(context_ != nullptr);
}
SameSignatureSecurityPolicy::~SameSignatureSecurityPolicy() {
JNIEnv* env = GetEnv(jvm_);
env->DeleteLocalRef(context_);
}
bool SameSignatureSecurityPolicy::IsAuthorized(int uid) {
JNIEnv* env = GetEnv(jvm_);
bool result = grpc_binder::IsSignatureMatch(env, context_, getuid(), uid);
if (result) {
gpr_log(GPR_INFO, "uid %d and uid %d passed SameSignature check", getuid(),
uid);
} else {
gpr_log(GPR_ERROR, "uid %d and uid %d failed SameSignature check", getuid(),
uid);
}
return result;
}
#endif
} // namespace binder
} // namespace experimental
} // namespace grpc

Loading…
Cancel
Save