summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gampe2014-08-20 00:31:31 -0500
committerAndreas Gampe2014-08-22 03:44:06 -0500
commit049249ce7addafaa0bd09480cd8858cd2c54138f (patch)
treed97559db3a4658231163f816ebc05d9e92ad86da /libnativebridge
parenta59b6ac6973b3f5ce1246bf73969094779436892 (diff)
downloadplatform-system-core-049249ce7addafaa0bd09480cd8858cd2c54138f.tar.gz
platform-system-core-049249ce7addafaa0bd09480cd8858cd2c54138f.tar.xz
platform-system-core-049249ce7addafaa0bd09480cd8858cd2c54138f.zip
NativeBridge: Tighten security on libnativebridge
Do not allow arbitrary paths for the native bridge - only allow simple names. Do not allow re-setup of the native bridge. Bug: 16404669 (cherry picked from commit cd2ef4c1af69727231b84ebc82864c170ff0e8ad) Change-Id: Ie22de356d2307fe2758f9094a85d44e61a4098a1
Diffstat (limited to 'libnativebridge')
-rw-r--r--libnativebridge/Android.mk6
-rw-r--r--libnativebridge/native_bridge.cc101
-rw-r--r--libnativebridge/tests/Android.mk33
-rw-r--r--libnativebridge/tests/InvalidCharsNativeBridge_test.cpp40
-rw-r--r--libnativebridge/tests/NativeBridgeTest.h33
-rw-r--r--libnativebridge/tests/ReSetupNativeBridge_test.cpp32
-rw-r--r--libnativebridge/tests/UnavailableNativeBridge_test.cpp28
-rw-r--r--libnativebridge/tests/ValidNameNativeBridge_test.cpp33
8 files changed, 297 insertions, 9 deletions
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
index 017ce0248..9403fd2e1 100644
--- a/libnativebridge/Android.mk
+++ b/libnativebridge/Android.mk
@@ -10,10 +10,11 @@ include $(CLEAR_VARS)
10LOCAL_MODULE:= libnativebridge 10LOCAL_MODULE:= libnativebridge
11 11
12LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES) 12LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
13LOCAL_SHARED_LIBRARIES := liblog
13LOCAL_CLANG := true 14LOCAL_CLANG := true
14LOCAL_CPP_EXTENSION := .cc 15LOCAL_CPP_EXTENSION := .cc
15LOCAL_CFLAGS := -Werror 16LOCAL_CFLAGS := -Werror
16LOCAL_CPPFLAGS := -std=gnu++11 17LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
17LOCAL_LDFLAGS := -ldl 18LOCAL_LDFLAGS := -ldl
18LOCAL_MULTILIB := both 19LOCAL_MULTILIB := both
19 20
@@ -26,10 +27,11 @@ include $(CLEAR_VARS)
26LOCAL_MODULE:= libnativebridge 27LOCAL_MODULE:= libnativebridge
27 28
28LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES) 29LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
30LOCAL_SHARED_LIBRARIES := liblog
29LOCAL_CLANG := true 31LOCAL_CLANG := true
30LOCAL_CPP_EXTENSION := .cc 32LOCAL_CPP_EXTENSION := .cc
31LOCAL_CFLAGS := -Werror 33LOCAL_CFLAGS := -Werror
32LOCAL_CPPFLAGS := -std=gnu++11 34LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
33LOCAL_LDFLAGS := -ldl 35LOCAL_LDFLAGS := -ldl
34LOCAL_MULTILIB := both 36LOCAL_MULTILIB := both
35 37
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index ad4ee73d6..2205f453b 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -16,6 +16,7 @@
16 16
17#include "nativebridge/native_bridge.h" 17#include "nativebridge/native_bridge.h"
18 18
19#include <cutils/log.h>
19#include <dlfcn.h> 20#include <dlfcn.h>
20#include <stdio.h> 21#include <stdio.h>
21#include "utils/Mutex.h" 22#include "utils/Mutex.h"
@@ -28,27 +29,92 @@ static Mutex native_bridge_lock("native bridge lock");
28// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. 29// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
29static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf"; 30static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
30 31
31// The path of the library we are supposed to load. 32// The filename of the library we are supposed to load.
32static const char* native_bridge_library_path = nullptr; 33static const char* native_bridge_library_filename = nullptr;
33 34
34// Whether a native bridge is available (loaded and ready). 35// Whether a native bridge is available (loaded and ready).
35static bool available = false; 36static bool available = false;
36// Whether we have already initialized (or tried to). 37// Whether we have already initialized (or tried to).
37static bool initialized = false; 38static bool initialized = false;
39// Whether we had an error at some point.
40static bool had_error = false;
38 41
39static NativeBridgeCallbacks* callbacks = nullptr; 42static NativeBridgeCallbacks* callbacks = nullptr;
40static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; 43static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr;
41 44
42void SetupNativeBridge(const char* nb_library_path, 45// Characters allowed in a native bridge filename. The first character must
46// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
47static bool CharacterAllowed(char c, bool first) {
48 if (first) {
49 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
50 } else {
51 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
52 (c == '.') || (c == '_') || (c == '-');
53 }
54}
55
56// We only allow simple names for the library. It is supposed to be a file in
57// /system/lib or /vendor/lib. Only allow a small range of characters, that is
58// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
59bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
60 const char* ptr = nb_library_filename;
61 if (*ptr == 0) {
62 // Emptry string. Allowed, means no native bridge.
63 return true;
64 } else {
65 // First character must be [a-zA-Z].
66 if (!CharacterAllowed(*ptr, true)) {
67 // Found an invalid fist character, don't accept.
68 ALOGE("Native bridge library %s has been rejected for first character %c", nb_library_filename, *ptr);
69 return false;
70 } else {
71 // For the rest, be more liberal.
72 ptr++;
73 while (*ptr != 0) {
74 if (!CharacterAllowed(*ptr, false)) {
75 // Found an invalid character, don't accept.
76 ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr);
77 return false;
78 }
79 ptr++;
80 }
81 }
82 return true;
83 }
84}
85
86void SetupNativeBridge(const char* nb_library_filename,
43 const NativeBridgeRuntimeCallbacks* runtime_cbs) { 87 const NativeBridgeRuntimeCallbacks* runtime_cbs) {
44 Mutex::Autolock auto_lock(native_bridge_lock); 88 Mutex::Autolock auto_lock(native_bridge_lock);
45 89
46 native_bridge_library_path = nb_library_path; 90 if (initialized || native_bridge_library_filename != nullptr) {
91 // Setup has been called before. Ignore this call.
92 ALOGW("Called SetupNativeBridge for an already set up native bridge.");
93 // Note: counts as an error, even though the bridge may be functional.
94 had_error = true;
95 return;
96 }
97
47 runtime_callbacks = runtime_cbs; 98 runtime_callbacks = runtime_cbs;
48 99
49 if (native_bridge_library_path == nullptr) { 100 if (nb_library_filename == nullptr) {
50 initialized = true;
51 available = false; 101 available = false;
102 initialized = true;
103 } else {
104 // Check whether it's an empty string.
105 if (*nb_library_filename == 0) {
106 available = false;
107 initialized = true;
108 } else if (!NativeBridgeNameAcceptable(nb_library_filename)) {
109 available = false;
110 initialized = true;
111 had_error = true;
112 }
113
114 if (!initialized) {
115 // Didn't find a name error or empty string, assign it.
116 native_bridge_library_filename = nb_library_filename;
117 }
52 } 118 }
53} 119}
54 120
@@ -62,7 +128,15 @@ static bool NativeBridgeInitialize() {
62 128
63 available = false; 129 available = false;
64 130
65 void* handle = dlopen(native_bridge_library_path, RTLD_LAZY); 131 if (native_bridge_library_filename == nullptr) {
132 // Called initialize without setup. dlopen has special semantics for nullptr input.
133 // So just call it a day here. This counts as an error.
134 initialized = true;
135 had_error = true;
136 return false;
137 }
138
139 void* handle = dlopen(native_bridge_library_filename, RTLD_LAZY);
66 if (handle != nullptr) { 140 if (handle != nullptr) {
67 callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, 141 callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
68 kNativeBridgeInterfaceSymbol)); 142 kNativeBridgeInterfaceSymbol));
@@ -72,8 +146,13 @@ static bool NativeBridgeInitialize() {
72 } 146 }
73 147
74 if (!available) { 148 if (!available) {
149 // If we fail initialization, this counts as an error.
150 had_error = true;
75 dlclose(handle); 151 dlclose(handle);
76 } 152 }
153 } else {
154 // Being unable to open the library counts as an error.
155 had_error = true;
77 } 156 }
78 157
79 initialized = true; 158 initialized = true;
@@ -81,6 +160,14 @@ static bool NativeBridgeInitialize() {
81 return available; 160 return available;
82} 161}
83 162
163bool NativeBridgeError() {
164 return had_error;
165}
166
167bool NativeBridgeAvailable() {
168 return NativeBridgeInitialize();
169}
170
84void* NativeBridgeLoadLibrary(const char* libpath, int flag) { 171void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
85 if (NativeBridgeInitialize()) { 172 if (NativeBridgeInitialize()) {
86 return callbacks->loadLibrary(libpath, flag); 173 return callbacks->loadLibrary(libpath, flag);
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
new file mode 100644
index 000000000..f58b8f7a9
--- /dev/null
+++ b/libnativebridge/tests/Android.mk
@@ -0,0 +1,33 @@
1# Build the unit tests.
2LOCAL_PATH := $(call my-dir)
3include $(CLEAR_VARS)
4
5# Build the unit tests.
6test_src_files := \
7 InvalidCharsNativeBridge_test.cpp \
8 ReSetupNativeBridge_test.cpp \
9 UnavailableNativeBridge_test.cpp \
10 ValidNameNativeBridge_test.cpp
11
12shared_libraries := \
13 libnativebridge
14
15$(foreach file,$(test_src_files), \
16 $(eval include $(CLEAR_VARS)) \
17 $(eval LOCAL_CLANG := true) \
18 $(eval LOCAL_CPPFLAGS := -std=gnu++11) \
19 $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
20 $(eval LOCAL_SRC_FILES := $(file)) \
21 $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
22 $(eval include $(BUILD_NATIVE_TEST)) \
23)
24
25$(foreach file,$(test_src_files), \
26 $(eval include $(CLEAR_VARS)) \
27 $(eval LOCAL_CLANG := true) \
28 $(eval LOCAL_CPPFLAGS := -std=gnu++11) \
29 $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
30 $(eval LOCAL_SRC_FILES := $(file)) \
31 $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
32 $(eval include $(BUILD_HOST_NATIVE_TEST)) \
33)
diff --git a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
new file mode 100644
index 000000000..f37e9c158
--- /dev/null
+++ b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
@@ -0,0 +1,40 @@
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "NativeBridgeTest.h"
18
19namespace android {
20
21static const char* kTestName = "../librandom$@-bridge_not.existing.so";
22
23TEST_F(NativeBridgeTest, InvalidChars) {
24 // Do one test actually calling setup.
25 EXPECT_EQ(false, NativeBridgeError());
26 SetupNativeBridge(kTestName, nullptr);
27 // This should lead to an error for invalid characters.
28 EXPECT_EQ(true, NativeBridgeError());
29
30 // Further tests need to use NativeBridgeNameAcceptable, as the error
31 // state can't be changed back.
32 EXPECT_EQ(false, NativeBridgeNameAcceptable("."));
33 EXPECT_EQ(false, NativeBridgeNameAcceptable(".."));
34 EXPECT_EQ(false, NativeBridgeNameAcceptable("_"));
35 EXPECT_EQ(false, NativeBridgeNameAcceptable("-"));
36 EXPECT_EQ(false, NativeBridgeNameAcceptable("lib@.so"));
37 EXPECT_EQ(false, NativeBridgeNameAcceptable("lib$.so"));
38}
39
40} // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
new file mode 100644
index 000000000..0d731cb11
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -0,0 +1,33 @@
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef NATIVE_BRIDGE_TEST_H_
18#define NATIVE_BRIDGE_TEST_H_
19
20#define LOG_TAG "NativeBridge_test"
21
22#include <nativebridge/native_bridge.h>
23#include <gtest/gtest.h>
24
25namespace android {
26
27class NativeBridgeTest : public testing::Test {
28};
29
30}; // namespace android
31
32#endif // NATIVE_BRIDGE_H_
33
diff --git a/libnativebridge/tests/ReSetupNativeBridge_test.cpp b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
new file mode 100644
index 000000000..ef5bfceb8
--- /dev/null
+++ b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
@@ -0,0 +1,32 @@
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "NativeBridgeTest.h"
18
19namespace android {
20
21static const char* kTestName = "librandom-bridge_not.existing.so";
22
23TEST_F(NativeBridgeTest, ReSetup) {
24 EXPECT_EQ(false, NativeBridgeError());
25 SetupNativeBridge(kTestName, nullptr);
26 EXPECT_EQ(false, NativeBridgeError());
27 SetupNativeBridge(kTestName, nullptr);
28 // This should lead to an error for trying to re-setup a native bridge.
29 EXPECT_EQ(true, NativeBridgeError());
30}
31
32} // namespace android
diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
new file mode 100644
index 000000000..27d12336c
--- /dev/null
+++ b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
@@ -0,0 +1,28 @@
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "NativeBridgeTest.h"
18
19namespace android {
20
21TEST_F(NativeBridgeTest, NoNativeBridge) {
22 EXPECT_EQ(false, NativeBridgeAvailable());
23 // This should lead to an error for trying to initialize a not-setup
24 // native bridge.
25 EXPECT_EQ(true, NativeBridgeError());
26}
27
28} // namespace android
diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
new file mode 100644
index 000000000..3e019232d
--- /dev/null
+++ b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
@@ -0,0 +1,33 @@
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <NativeBridgeTest.h>
18
19namespace android {
20
21static const char* kTestName = "librandom-bridge_not.existing.so";
22
23TEST_F(NativeBridgeTest, ValidName) {
24 EXPECT_EQ(false, NativeBridgeError());
25 SetupNativeBridge(kTestName, nullptr);
26 EXPECT_EQ(false, NativeBridgeError());
27 EXPECT_EQ(false, NativeBridgeAvailable());
28 // This should lead to an error for trying to initialize a not-existing
29 // native bridge.
30 EXPECT_EQ(true, NativeBridgeError());
31}
32
33} // namespace android