diff options
| author | 2021-09-14 00:41:10 +0000 | |
|---|---|---|
| committer | 2021-09-14 00:41:10 +0000 | |
| commit | a3612f324c0d4c296930123ba07e915bbebb6dc2 (patch) | |
| tree | 612ba7de6bbe155a5c0cfa57c2b54103466cac54 | |
| parent | e9b2010fb1a8dcd0f23cac1731f510b0bfa4301f (diff) | |
| parent | 9734cfc736b89134987b98e35210e304ca618b75 (diff) | |
Merge changes Ifaea4598,I78b4d2b1,I6c1f0f88,I6b4d0ebc,I5c17f464
* changes:
binder: CertificateFormat -> RpcCertificateFormat.
binder: Support DER format certificates.
binder: implement simple TLS verification for testing
binder: X509 are serialized to vector<uint8_t>
binder: delegate cert verification to RpcCertificateVerifier
| -rw-r--r-- | libs/binder/Android.bp | 1 | ||||
| -rw-r--r-- | libs/binder/RpcCertificateUtils.cpp | 78 | ||||
| -rw-r--r-- | libs/binder/RpcServer.cpp | 2 | ||||
| -rw-r--r-- | libs/binder/RpcSession.cpp | 2 | ||||
| -rw-r--r-- | libs/binder/RpcTransportRaw.cpp | 2 | ||||
| -rw-r--r-- | libs/binder/RpcTransportTls.cpp | 57 | ||||
| -rw-r--r-- | libs/binder/include/binder/RpcCertificateFormat.h | 41 | ||||
| -rw-r--r-- | libs/binder/include/binder/RpcServer.h | 2 | ||||
| -rw-r--r-- | libs/binder/include/binder/RpcSession.h | 4 | ||||
| -rw-r--r-- | libs/binder/include/binder/RpcTransport.h | 5 | ||||
| -rw-r--r-- | libs/binder/include_tls/binder/RpcCertificateUtils.h | 34 | ||||
| -rw-r--r-- | libs/binder/tests/Android.bp | 3 | ||||
| -rw-r--r-- | libs/binder/tests/BinderRpcTestClientInfo.aidl (renamed from libs/binder/include/binder/CertificateFormat.h) | 15 | ||||
| -rw-r--r-- | libs/binder/tests/BinderRpcTestServerInfo.aidl | 22 | ||||
| -rw-r--r-- | libs/binder/tests/ParcelableCertificateData.aidl | 19 | ||||
| -rw-r--r-- | libs/binder/tests/RpcCertificateVerifierSimple.cpp | 24 | ||||
| -rw-r--r-- | libs/binder/tests/RpcCertificateVerifierSimple.h | 24 | ||||
| -rw-r--r-- | libs/binder/tests/binderRpcTest.cpp | 436 |
18 files changed, 713 insertions, 58 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index b0d7478b96..9bca1f3482 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -262,6 +262,7 @@ cc_defaults { ], srcs: [ "RpcTransportTls.cpp", + "RpcCertificateUtils.cpp", ], } diff --git a/libs/binder/RpcCertificateUtils.cpp b/libs/binder/RpcCertificateUtils.cpp new file mode 100644 index 0000000000..d91736cca2 --- /dev/null +++ b/libs/binder/RpcCertificateUtils.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RpcCertificateUtils" +#include <log/log.h> + +#include <binder/RpcCertificateUtils.h> + +#include "Utils.h" + +namespace android { + +namespace { + +bssl::UniquePtr<X509> fromPem(const std::vector<uint8_t>& cert) { + if (cert.size() > std::numeric_limits<int>::max()) return nullptr; + bssl::UniquePtr<BIO> certBio(BIO_new_mem_buf(cert.data(), static_cast<int>(cert.size()))); + return bssl::UniquePtr<X509>(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr)); +} + +bssl::UniquePtr<X509> fromDer(const std::vector<uint8_t>& cert) { + if (cert.size() > std::numeric_limits<long>::max()) return nullptr; + const unsigned char* data = cert.data(); + auto expectedEnd = data + cert.size(); + bssl::UniquePtr<X509> ret(d2i_X509(nullptr, &data, static_cast<long>(cert.size()))); + if (data != expectedEnd) { + ALOGE("%s: %td bytes remaining!", __PRETTY_FUNCTION__, expectedEnd - data); + return nullptr; + } + return ret; +} + +} // namespace + +bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert, + RpcCertificateFormat format) { + switch (format) { + case RpcCertificateFormat::PEM: + return fromPem(cert); + case RpcCertificateFormat::DER: + return fromDer(cert); + } + LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format)); +} + +std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format) { + bssl::UniquePtr<BIO> certBio(BIO_new(BIO_s_mem())); + switch (format) { + case RpcCertificateFormat::PEM: { + TEST_AND_RETURN({}, PEM_write_bio_X509(certBio.get(), x509)); + } break; + case RpcCertificateFormat::DER: { + TEST_AND_RETURN({}, i2d_X509_bio(certBio.get(), x509)); + } break; + default: { + LOG_ALWAYS_FATAL("Unsupported format %d", static_cast<int>(format)); + } + } + const uint8_t* data; + size_t len; + TEST_AND_RETURN({}, BIO_mem_contents(certBio.get(), &data, &len)); + return std::vector<uint8_t>(data, data + len); +} + +} // namespace android diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 7fa2f5764f..5733993b3b 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -141,7 +141,7 @@ sp<IBinder> RpcServer::getRootObject() { return ret; } -std::string RpcServer::getCertificate(CertificateFormat format) { +std::vector<uint8_t> RpcServer::getCertificate(RpcCertificateFormat format) { std::lock_guard<std::mutex> _l(mLock); return mCtx->getCertificate(format); } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index c5a8dd1ddf..9395b505e1 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -703,7 +703,7 @@ bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { return false; } -std::string RpcSession::getCertificate(CertificateFormat format) { +std::vector<uint8_t> RpcSession::getCertificate(RpcCertificateFormat format) { return mCtx->getCertificate(format); } diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index 62c953007a..c012df84f7 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -111,7 +111,7 @@ public: std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger*) const { return std::make_unique<RpcTransportRaw>(std::move(fd)); } - std::string getCertificate(CertificateFormat) const override { return {}; } + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; } }; } // namespace diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index e9a494f9af..d40cfc84fc 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -22,6 +22,7 @@ #include <openssl/bn.h> #include <openssl/ssl.h> +#include <binder/RpcCertificateUtils.h> #include <binder/RpcTransportTls.h> #include "FdTrigger.h" @@ -446,25 +447,54 @@ class RpcTransportCtxTls : public RpcTransportCtx { public: template <typename Impl, typename = std::enable_if_t<std::is_base_of_v<RpcTransportCtxTls, Impl>>> - static std::unique_ptr<RpcTransportCtxTls> create(); + static std::unique_ptr<RpcTransportCtxTls> create( + std::shared_ptr<RpcCertificateVerifier> verifier); std::unique_ptr<RpcTransport> newTransport(android::base::unique_fd fd, FdTrigger* fdTrigger) const override; - std::string getCertificate(CertificateFormat) const override; + std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override; protected: + static ssl_verify_result_t sslCustomVerify(SSL* ssl, uint8_t* outAlert); virtual void preHandshake(Ssl* ssl) const = 0; bssl::UniquePtr<SSL_CTX> mCtx; + std::shared_ptr<RpcCertificateVerifier> mCertVerifier; }; -std::string RpcTransportCtxTls::getCertificate(CertificateFormat) const { - // TODO(b/195166979): return certificate here - return {}; +std::vector<uint8_t> RpcTransportCtxTls::getCertificate(RpcCertificateFormat format) const { + X509* x509 = SSL_CTX_get0_certificate(mCtx.get()); // does not own + return serializeCertificate(x509, format); +} + +// Verify by comparing the leaf of peer certificate with every certificate in +// mTrustedPeerCertificates. Does not support certificate chains. +ssl_verify_result_t RpcTransportCtxTls::sslCustomVerify(SSL* ssl, uint8_t* outAlert) { + LOG_ALWAYS_FATAL_IF(outAlert == nullptr); + const char* logPrefix = SSL_is_server(ssl) ? "Server" : "Client"; + + bssl::UniquePtr<X509> peerCert(SSL_get_peer_certificate(ssl)); // Does not set error queue + LOG_ALWAYS_FATAL_IF(peerCert == nullptr, + "%s: libssl should not ask to verify non-existing cert", logPrefix); + + auto ctx = SSL_get_SSL_CTX(ssl); // Does not set error queue + LOG_ALWAYS_FATAL_IF(ctx == nullptr); + // void* -> RpcTransportCtxTls* + auto rpcTransportCtxTls = reinterpret_cast<RpcTransportCtxTls*>(SSL_CTX_get_app_data(ctx)); + LOG_ALWAYS_FATAL_IF(rpcTransportCtxTls == nullptr); + + status_t verifyStatus = rpcTransportCtxTls->mCertVerifier->verify(peerCert.get(), outAlert); + if (verifyStatus == OK) { + return ssl_verify_ok; + } + LOG_TLS_DETAIL("%s: Failed to verify client: status = %s, alert = %s", logPrefix, + statusToString(verifyStatus).c_str(), SSL_alert_desc_string_long(*outAlert)); + return ssl_verify_invalid; } // Common implementation for creating server and client contexts. The child class, |Impl|, is // provided as a template argument so that this function can initialize an |Impl| object. template <typename Impl, typename> -std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create() { +std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create( + std::shared_ptr<RpcCertificateVerifier> verifier) { bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); TEST_AND_RETURN(nullptr, ctx != nullptr); @@ -475,10 +505,10 @@ std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create() { TEST_AND_RETURN(nullptr, SSL_CTX_use_PrivateKey(ctx.get(), evp_pkey.get())); TEST_AND_RETURN(nullptr, SSL_CTX_use_certificate(ctx.get(), cert.get())); - // TODO(b/195166979): peer should send certificate in a different channel, and this class - // should verify it here. - SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER, - [](SSL*, uint8_t*) -> ssl_verify_result_t { return ssl_verify_ok; }); + // Enable two-way authentication by setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT on server. + // Client ignores SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag. + SSL_CTX_set_custom_verify(ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + sslCustomVerify); // Require at least TLS 1.3 TEST_AND_RETURN(nullptr, SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION)); @@ -488,7 +518,10 @@ std::unique_ptr<RpcTransportCtxTls> RpcTransportCtxTls::create() { } auto ret = std::make_unique<Impl>(); + // RpcTransportCtxTls* -> void* + TEST_AND_RETURN(nullptr, SSL_CTX_set_app_data(ctx.get(), reinterpret_cast<void*>(ret.get()))); ret->mCtx = std::move(ctx); + ret->mCertVerifier = std::move(verifier); return ret; } @@ -520,11 +553,11 @@ protected: } // namespace std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newServerCtx() const { - return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(); + return android::RpcTransportCtxTls::create<RpcTransportCtxTlsServer>(mCertVerifier); } std::unique_ptr<RpcTransportCtx> RpcTransportCtxFactoryTls::newClientCtx() const { - return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>(); + return android::RpcTransportCtxTls::create<RpcTransportCtxTlsClient>(mCertVerifier); } const char* RpcTransportCtxFactoryTls::toCString() const { diff --git a/libs/binder/include/binder/RpcCertificateFormat.h b/libs/binder/include/binder/RpcCertificateFormat.h new file mode 100644 index 0000000000..bc9d814773 --- /dev/null +++ b/libs/binder/include/binder/RpcCertificateFormat.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Formats for serializing TLS certificate. + +#pragma once + +#include <string> + +namespace android { + +enum class RpcCertificateFormat { + PEM, + DER, +}; + +static inline std::string PrintToString(RpcCertificateFormat format) { + switch (format) { + case RpcCertificateFormat::PEM: + return "PEM"; + case RpcCertificateFormat::DER: + return "DER"; + default: + return "<unknown>"; + } +} + +} // namespace android diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 5229cfeb5b..fb2cf23cd0 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -135,7 +135,7 @@ public: /** * See RpcTransportCtx::getCertificate */ - std::string getCertificate(CertificateFormat); + std::vector<uint8_t> getCertificate(RpcCertificateFormat); /** * Runs join() in a background thread. Immediately returns. diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index 91db637f43..71eb223a07 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -53,7 +53,7 @@ public: // Create an RpcSession with default configuration (raw sockets). static sp<RpcSession> make(); - // Create an RpcSession with the given configuration. |serverCertificateFormat| and + // Create an RpcSession with the given configuration. |serverRpcCertificateFormat| and // |serverCertificate| must have values or be nullopt simultaneously. If they have values, set // server certificate. static sp<RpcSession> make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory); @@ -131,7 +131,7 @@ public: /** * See RpcTransportCtx::getCertificate */ - std::string getCertificate(CertificateFormat); + std::vector<uint8_t> getCertificate(RpcCertificateFormat); /** * Shuts down the service. diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index da132a1abb..1c0bb18e4a 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -25,7 +25,7 @@ #include <android-base/unique_fd.h> #include <utils/Errors.h> -#include <binder/CertificateFormat.h> +#include <binder/RpcCertificateFormat.h> namespace android { @@ -73,7 +73,8 @@ public: // Implementation details: // - For raw sockets, this always returns empty string. // - For TLS, this returns the certificate. See RpcTransportTls for details. - [[nodiscard]] virtual std::string getCertificate(CertificateFormat format) const = 0; + [[nodiscard]] virtual std::vector<uint8_t> getCertificate( + RpcCertificateFormat format) const = 0; protected: RpcTransportCtx() = default; diff --git a/libs/binder/include_tls/binder/RpcCertificateUtils.h b/libs/binder/include_tls/binder/RpcCertificateUtils.h new file mode 100644 index 0000000000..8d07835344 --- /dev/null +++ b/libs/binder/include_tls/binder/RpcCertificateUtils.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Utilities for serializing and deserializing X509 certificates. + +#pragma once + +#include <vector> + +#include <openssl/ssl.h> + +#include <binder/RpcCertificateFormat.h> + +namespace android { + +bssl::UniquePtr<X509> deserializeCertificate(const std::vector<uint8_t>& cert, + RpcCertificateFormat format); + +std::vector<uint8_t> serializeCertificate(X509* x509, RpcCertificateFormat format); + +} // namespace android diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index a9bc15d5b3..1968058aed 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -120,9 +120,12 @@ aidl_interface { host_supported: true, unstable: true, srcs: [ + "BinderRpcTestClientInfo.aidl", + "BinderRpcTestServerInfo.aidl", "IBinderRpcCallback.aidl", "IBinderRpcSession.aidl", "IBinderRpcTest.aidl", + "ParcelableCertificateData.aidl", ], backend: { java: { diff --git a/libs/binder/include/binder/CertificateFormat.h b/libs/binder/tests/BinderRpcTestClientInfo.aidl index 4f7e71eee8..b4baebc013 100644 --- a/libs/binder/include/binder/CertificateFormat.h +++ b/libs/binder/tests/BinderRpcTestClientInfo.aidl @@ -14,15 +14,8 @@ * limitations under the License. */ -// Formats for serializing TLS certificate. +import ParcelableCertificateData; -#pragma once - -namespace android { - -enum class CertificateFormat { - PEM, - // TODO(b/195166979): support other formats, e.g. DER -}; - -} // namespace android +parcelable BinderRpcTestClientInfo { + ParcelableCertificateData[] certs; +} diff --git a/libs/binder/tests/BinderRpcTestServerInfo.aidl b/libs/binder/tests/BinderRpcTestServerInfo.aidl new file mode 100644 index 0000000000..00dc0bcf81 --- /dev/null +++ b/libs/binder/tests/BinderRpcTestServerInfo.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import ParcelableCertificateData; + +parcelable BinderRpcTestServerInfo { + long port; + ParcelableCertificateData cert; +} diff --git a/libs/binder/tests/ParcelableCertificateData.aidl b/libs/binder/tests/ParcelableCertificateData.aidl new file mode 100644 index 0000000000..38c382eb1f --- /dev/null +++ b/libs/binder/tests/ParcelableCertificateData.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +parcelable ParcelableCertificateData { + byte[] data; +} diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.cpp b/libs/binder/tests/RpcCertificateVerifierSimple.cpp index 68e7c6567c..4694d1b2e0 100644 --- a/libs/binder/tests/RpcCertificateVerifierSimple.cpp +++ b/libs/binder/tests/RpcCertificateVerifierSimple.cpp @@ -16,12 +16,32 @@ #define LOG_TAG "RpcCertificateVerifierSimple" #include <log/log.h> +#include <binder/RpcCertificateUtils.h> + #include "RpcCertificateVerifierSimple.h" namespace android { -status_t RpcCertificateVerifierSimple::verify(const X509*, uint8_t*) { - // TODO(b/195166979): implement this +status_t RpcCertificateVerifierSimple::verify(const X509* peerCert, uint8_t* outAlert) { + std::lock_guard<std::mutex> lock(mMutex); + for (const auto& trustedCert : mTrustedPeerCertificates) { + if (0 == X509_cmp(trustedCert.get(), peerCert)) { + return OK; + } + } + *outAlert = SSL_AD_CERTIFICATE_UNKNOWN; + return PERMISSION_DENIED; +} + +status_t RpcCertificateVerifierSimple::addTrustedPeerCertificate(RpcCertificateFormat format, + const std::vector<uint8_t>& cert) { + bssl::UniquePtr<X509> x509 = deserializeCertificate(cert, format); + if (x509 == nullptr) { + ALOGE("Certificate is not in the proper format %s", PrintToString(format).c_str()); + return BAD_VALUE; + } + std::lock_guard<std::mutex> lock(mMutex); + mTrustedPeerCertificates.push_back(std::move(x509)); return OK; } diff --git a/libs/binder/tests/RpcCertificateVerifierSimple.h b/libs/binder/tests/RpcCertificateVerifierSimple.h index aff5c7cae5..1f2e531b5b 100644 --- a/libs/binder/tests/RpcCertificateVerifierSimple.h +++ b/libs/binder/tests/RpcCertificateVerifierSimple.h @@ -16,14 +16,38 @@ #pragma once +#include <mutex> +#include <string_view> +#include <vector> + +#include <openssl/ssl.h> + +#include <binder/RpcCertificateFormat.h> #include <binder/RpcCertificateVerifier.h> namespace android { // A simple certificate verifier for testing. +// Keep a list of leaf certificates as trusted. No certificate chain support. +// +// All APIs are thread-safe. However, if verify() and addTrustedPeerCertificate() are called +// simultaneously in different threads, it is not deterministic whether verify() will use the +// certificate being added. class RpcCertificateVerifierSimple : public RpcCertificateVerifier { public: status_t verify(const X509*, uint8_t*) override; + + // Add a trusted peer certificate. Peers presenting this certificate are accepted. + // + // Caller must ensure that RpcTransportCtx::newTransport() are called after all trusted peer + // certificates are added. Otherwise, RpcTransport-s created before may not trust peer + // certificates added later. + [[nodiscard]] status_t addTrustedPeerCertificate(RpcCertificateFormat format, + const std::vector<uint8_t>& cert); + +private: + std::mutex mMutex; // for below + std::vector<bssl::UniquePtr<X509>> mTrustedPeerCertificates; }; } // namespace android diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index dbf8899904..a4e37adde9 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <BinderRpcTestClientInfo.h> +#include <BinderRpcTestServerInfo.h> #include <BnBinderRpcCallback.h> #include <BnBinderRpcSession.h> #include <BnBinderRpcTest.h> @@ -40,15 +42,20 @@ #include <thread> #include <type_traits> +#include <poll.h> #include <sys/prctl.h> #include <unistd.h> +#include "../FdTrigger.h" #include "../RpcSocketAddress.h" // for testing preconnected clients #include "../RpcState.h" // for debugging #include "../vm_sockets.h" // for VMADDR_* #include "RpcCertificateVerifierSimple.h" using namespace std::chrono_literals; +using testing::AssertionFailure; +using testing::AssertionResult; +using testing::AssertionSuccess; namespace android { @@ -68,7 +75,6 @@ static inline std::unique_ptr<RpcTransportCtxFactory> newFactory( case RpcSecurity::RAW: return RpcTransportCtxFactoryRaw::make(); case RpcSecurity::TLS: { - // TODO(b/198833574): exchange keys and set proper verifier if (verifier == nullptr) { verifier = std::make_shared<RpcCertificateVerifierSimple>(); } @@ -311,14 +317,17 @@ sp<IBinder> MyBinderRpcTest::mHeldBinder; class Process { public: Process(Process&&) = default; - Process(const std::function<void(android::base::borrowed_fd /* writeEnd */)>& f) { - android::base::unique_fd writeEnd; - CHECK(android::base::Pipe(&mReadEnd, &writeEnd)) << strerror(errno); + Process(const std::function<void(android::base::borrowed_fd /* writeEnd */, + android::base::borrowed_fd /* readEnd */)>& f) { + android::base::unique_fd childWriteEnd; + android::base::unique_fd childReadEnd; + CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd)) << strerror(errno); + CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd)) << strerror(errno); if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); - f(writeEnd); + f(childWriteEnd, childReadEnd); exit(0); } @@ -329,16 +338,20 @@ public: } } android::base::borrowed_fd readEnd() { return mReadEnd; } + android::base::borrowed_fd writeEnd() { return mWriteEnd; } private: pid_t mPid = 0; android::base::unique_fd mReadEnd; + android::base::unique_fd mWriteEnd; }; static std::string allocateSocketAddress() { static size_t id = 0; std::string temp = getenv("TMPDIR") ?: "/tmp"; - return temp + "/binderRpcTest_" + std::to_string(id++); + auto ret = temp + "/binderRpcTest_" + std::to_string(id++); + unlink(ret.c_str()); + return ret; }; static unsigned int allocateVsockPort() { @@ -441,16 +454,17 @@ static inline std::string PrintToString(SocketType socketType) { } } -static base::unique_fd connectToUds(const char* addrStr) { - UnixSocketAddress addr(addrStr); +static base::unique_fd connectTo(const RpcSocketAddress& addr) { base::unique_fd serverFd( TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); int savedErrno = errno; - CHECK(serverFd.ok()) << "Could not create socket " << addrStr << ": " << strerror(savedErrno); + CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": " + << strerror(savedErrno); if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { int savedErrno = errno; - LOG(FATAL) << "Could not connect to socket " << addrStr << ": " << strerror(savedErrno); + LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": " + << strerror(savedErrno); } return serverFd; } @@ -468,6 +482,37 @@ public: return PrintToString(type) + "_" + newFactory(security)->toCString(); } + static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { + uint64_t length = str.length(); + CHECK(android::base::WriteFully(fd, &length, sizeof(length))); + CHECK(android::base::WriteFully(fd, str.data(), str.length())); + } + + static inline std::string readString(android::base::borrowed_fd fd) { + uint64_t length; + CHECK(android::base::ReadFully(fd, &length, sizeof(length))); + std::string ret(length, '\0'); + CHECK(android::base::ReadFully(fd, ret.data(), length)); + return ret; + } + + static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) { + Parcel parcel; + CHECK_EQ(OK, parcelable.writeToParcel(&parcel)); + writeString(fd, + std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize())); + } + + template <typename T> + static inline T readFromFd(android::base::borrowed_fd fd) { + std::string data = readString(fd); + Parcel parcel; + CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size())); + T object; + CHECK_EQ(OK, object.readFromParcel(&parcel)); + return object; + } + // This creates a new process serving an interface on a certain number of // threads. ProcessSession createRpcTestSocketServerProcess( @@ -479,11 +524,12 @@ public: unsigned int vsockPort = allocateVsockPort(); std::string addr = allocateSocketAddress(); - unlink(addr.c_str()); auto ret = ProcessSession{ - .host = Process([&](android::base::borrowed_fd writeEnd) { - sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity)); + .host = Process([&](android::base::borrowed_fd writeEnd, + android::base::borrowed_fd readEnd) { + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); server->setMaxThreads(options.numThreads); @@ -508,7 +554,20 @@ public: LOG_ALWAYS_FATAL("Unknown socket type"); } - CHECK(android::base::WriteFully(writeEnd, &outPort, sizeof(outPort))); + BinderRpcTestServerInfo serverInfo; + serverInfo.port = static_cast<int64_t>(outPort); + serverInfo.cert.data = server->getCertificate(RpcCertificateFormat::PEM); + writeToFd(writeEnd, serverInfo); + auto clientInfo = readFromFd<BinderRpcTestClientInfo>(readEnd); + + if (rpcSecurity == RpcSecurity::TLS) { + for (const auto& clientCert : clientInfo.certs) { + CHECK_EQ(OK, + certVerifier + ->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + clientCert.data)); + } + } configure(server); @@ -519,23 +578,41 @@ public: }), }; - // always read socket, so that we have waited for the server to start - unsigned int outPort = 0; - CHECK(android::base::ReadFully(ret.host.readEnd(), &outPort, sizeof(outPort))); + std::vector<sp<RpcSession>> sessions; + auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); + for (size_t i = 0; i < options.numSessions; i++) { + sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier))); + } + + auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret.host.readEnd()); + BinderRpcTestClientInfo clientInfo; + for (const auto& session : sessions) { + auto& parcelableCert = clientInfo.certs.emplace_back(); + parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM); + } + writeToFd(ret.host.writeEnd(), clientInfo); + + CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max()); if (socketType == SocketType::INET) { - CHECK_NE(0, outPort); + CHECK_NE(0, serverInfo.port); + } + + if (rpcSecurity == RpcSecurity::TLS) { + const auto& serverCert = serverInfo.cert.data; + CHECK_EQ(OK, + certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, + serverCert)); } status_t status; - for (size_t i = 0; i < options.numSessions; i++) { - sp<RpcSession> session = RpcSession::make(newFactory(rpcSecurity)); + for (const auto& session : sessions) { session->setMaxThreads(options.numIncomingConnections); switch (socketType) { case SocketType::PRECONNECTED: status = session->setupPreconnectedClient({}, [=]() { - return connectToUds(addr.c_str()); + return connectTo(UnixSocketAddress(addr.c_str())); }); if (status == OK) goto success; break; @@ -548,7 +625,7 @@ public: if (status == OK) goto success; break; case SocketType::INET: - status = session->setupInetClient("127.0.0.1", outPort); + status = session->setupInetClient("127.0.0.1", serverInfo.port); if (status == OK) goto success; break; default: @@ -1221,8 +1298,10 @@ static bool testSupportVsockLoopback() { return status == OK; } -static std::vector<SocketType> testSocketTypes() { - std::vector<SocketType> ret = {SocketType::PRECONNECTED, SocketType::UNIX, SocketType::INET}; +static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { + std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET}; + + if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED); static bool hasVsockLoopback = testSupportVsockLoopback(); @@ -1291,7 +1370,6 @@ private: TEST_P(BinderRpcSimple, Shutdown) { auto addr = allocateSocketAddress(); - unlink(addr.c_str()); auto server = RpcServer::make(newFactory(GetParam())); server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); @@ -1356,6 +1434,314 @@ TEST(BinderRpc, Java) { INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), BinderRpcSimple::PrintTestParam); +class RpcTransportTest + : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, RpcCertificateFormat>> { +public: + using ConnectToServer = std::function<base::unique_fd()>; + static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { + auto [socketType, rpcSecurity, certificateFormat] = info.param; + return PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString() + "_" + + PrintToString(certificateFormat); + } + void TearDown() override { + for (auto& server : mServers) server->shutdown(); + } + + // A server that handles client socket connections. + class Server { + public: + explicit Server() {} + Server(Server&&) = default; + ~Server() { shutdown(); } + [[nodiscard]] AssertionResult setUp() { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto rpcServer = RpcServer::make(newFactory(rpcSecurity)); + rpcServer->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); + switch (socketType) { + case SocketType::PRECONNECTED: { + return AssertionFailure() << "Not supported by this test"; + } break; + case SocketType::UNIX: { + auto addr = allocateSocketAddress(); + auto status = rpcServer->setupUnixDomainServer(addr.c_str()); + if (status != OK) { + return AssertionFailure() + << "setupUnixDomainServer: " << statusToString(status); + } + mConnectToServer = [addr] { + return connectTo(UnixSocketAddress(addr.c_str())); + }; + } break; + case SocketType::VSOCK: { + auto port = allocateVsockPort(); + auto status = rpcServer->setupVsockServer(port); + if (status != OK) { + return AssertionFailure() << "setupVsockServer: " << statusToString(status); + } + mConnectToServer = [port] { + return connectTo(VsockSocketAddress(VMADDR_CID_LOCAL, port)); + }; + } break; + case SocketType::INET: { + unsigned int port; + auto status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); + if (status != OK) { + return AssertionFailure() << "setupInetServer: " << statusToString(status); + } + mConnectToServer = [port] { + const char* addr = kLocalInetAddress; + auto aiStart = InetSocketAddress::getAddrInfo(addr, port); + if (aiStart == nullptr) return base::unique_fd{}; + for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { + auto fd = connectTo( + InetSocketAddress(ai->ai_addr, ai->ai_addrlen, addr, port)); + if (fd.ok()) return fd; + } + ALOGE("None of the socket address resolved for %s:%u can be connected", + addr, port); + return base::unique_fd{}; + }; + } + } + mFd = rpcServer->releaseServer(); + if (!mFd.ok()) return AssertionFailure() << "releaseServer returns invalid fd"; + mCtx = newFactory(rpcSecurity, mCertVerifier)->newServerCtx(); + if (mCtx == nullptr) return AssertionFailure() << "newServerCtx"; + mSetup = true; + return AssertionSuccess(); + } + RpcTransportCtx* getCtx() const { return mCtx.get(); } + std::shared_ptr<RpcCertificateVerifierSimple> getCertVerifier() const { + return mCertVerifier; + } + ConnectToServer getConnectToServerFn() { return mConnectToServer; } + void start() { + LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!"); + mThread = std::make_unique<std::thread>(&Server::run, this); + } + void run() { + LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!"); + + std::vector<std::thread> threads; + while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) { + base::unique_fd acceptedFd( + TEMP_FAILURE_RETRY(accept4(mFd.get(), nullptr, nullptr /*length*/, + SOCK_CLOEXEC | SOCK_NONBLOCK))); + threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd)); + } + + for (auto& thread : threads) thread.join(); + } + void handleOne(android::base::unique_fd acceptedFd) { + ASSERT_TRUE(acceptedFd.ok()); + auto serverTransport = mCtx->newTransport(std::move(acceptedFd), mFdTrigger.get()); + if (serverTransport == nullptr) return; // handshake failed + std::string message(kMessage); + ASSERT_EQ(OK, + serverTransport->interruptableWriteFully(mFdTrigger.get(), message.data(), + message.size())); + } + void shutdown() { + mFdTrigger->trigger(); + if (mThread != nullptr) { + mThread->join(); + mThread = nullptr; + } + } + + private: + std::unique_ptr<std::thread> mThread; + ConnectToServer mConnectToServer; + std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make(); + base::unique_fd mFd; + std::unique_ptr<RpcTransportCtx> mCtx; + std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier = + std::make_shared<RpcCertificateVerifierSimple>(); + bool mSetup = false; + }; + + class Client { + public: + explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {} + Client(Client&&) = default; + [[nodiscard]] AssertionResult setUp() { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + mFd = mConnectToServer(); + if (!mFd.ok()) return AssertionFailure() << "Cannot connect to server"; + mFdTrigger = FdTrigger::make(); + mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx(); + if (mCtx == nullptr) return AssertionFailure() << "newClientCtx"; + return AssertionSuccess(); + } + RpcTransportCtx* getCtx() const { return mCtx.get(); } + std::shared_ptr<RpcCertificateVerifierSimple> getCertVerifier() const { + return mCertVerifier; + } + void run(bool handshakeOk = true, bool readOk = true) { + auto clientTransport = mCtx->newTransport(std::move(mFd), mFdTrigger.get()); + if (clientTransport == nullptr) { + ASSERT_FALSE(handshakeOk) << "newTransport returns nullptr, but it shouldn't"; + return; + } + ASSERT_TRUE(handshakeOk) << "newTransport does not return nullptr, but it should"; + std::string expectedMessage(kMessage); + std::string readMessage(expectedMessage.size(), '\0'); + status_t readStatus = + clientTransport->interruptableReadFully(mFdTrigger.get(), readMessage.data(), + readMessage.size()); + if (readOk) { + ASSERT_EQ(OK, readStatus); + ASSERT_EQ(readMessage, expectedMessage); + } else { + ASSERT_NE(OK, readStatus); + } + } + + private: + ConnectToServer mConnectToServer; + base::unique_fd mFd; + std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make(); + std::unique_ptr<RpcTransportCtx> mCtx; + std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier = + std::make_shared<RpcCertificateVerifierSimple>(); + }; + + // Make A trust B. + template <typename A, typename B> + status_t trust(A* a, B* b) { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + if (rpcSecurity != RpcSecurity::TLS) return OK; + auto bCert = b->getCtx()->getCertificate(certificateFormat); + return a->getCertVerifier()->addTrustedPeerCertificate(certificateFormat, bCert); + } + + static constexpr const char* kMessage = "hello"; + std::vector<std::unique_ptr<Server>> mServers; +}; + +TEST_P(RpcTransportTest, GoodCertificate) { + auto server = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(server->setUp()); + + Client client(server->getConnectToServerFn()); + ASSERT_TRUE(client.setUp()); + + ASSERT_EQ(OK, trust(&client, server)); + ASSERT_EQ(OK, trust(server, &client)); + + server->start(); + client.run(); +} + +TEST_P(RpcTransportTest, MultipleClients) { + auto server = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(server->setUp()); + + std::vector<Client> clients; + for (int i = 0; i < 2; i++) { + auto& client = clients.emplace_back(server->getConnectToServerFn()); + ASSERT_TRUE(client.setUp()); + ASSERT_EQ(OK, trust(&client, server)); + ASSERT_EQ(OK, trust(server, &client)); + } + + server->start(); + for (auto& client : clients) client.run(); +} + +TEST_P(RpcTransportTest, UntrustedServer) { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + + auto untrustedServer = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(untrustedServer->setUp()); + + Client client(untrustedServer->getConnectToServerFn()); + ASSERT_TRUE(client.setUp()); + + ASSERT_EQ(OK, trust(untrustedServer, &client)); + + untrustedServer->start(); + + // For TLS, this should reject the certificate. For RAW sockets, it should pass because + // the client can't verify the server's identity. + bool handshakeOk = rpcSecurity != RpcSecurity::TLS; + client.run(handshakeOk); +} +TEST_P(RpcTransportTest, MaliciousServer) { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto validServer = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(validServer->setUp()); + + auto maliciousServer = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(maliciousServer->setUp()); + + Client client(maliciousServer->getConnectToServerFn()); + ASSERT_TRUE(client.setUp()); + + ASSERT_EQ(OK, trust(&client, validServer)); + ASSERT_EQ(OK, trust(validServer, &client)); + ASSERT_EQ(OK, trust(maliciousServer, &client)); + + maliciousServer->start(); + + // For TLS, this should reject the certificate. For RAW sockets, it should pass because + // the client can't verify the server's identity. + bool handshakeOk = rpcSecurity != RpcSecurity::TLS; + client.run(handshakeOk); +} + +TEST_P(RpcTransportTest, UntrustedClient) { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto server = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(server->setUp()); + + Client client(server->getConnectToServerFn()); + ASSERT_TRUE(client.setUp()); + + ASSERT_EQ(OK, trust(&client, server)); + + server->start(); + + // For TLS, Client should be able to verify server's identity, so client should see + // do_handshake() successfully executed. However, server shouldn't be able to verify client's + // identity and should drop the connection, so client shouldn't be able to read anything. + bool readOk = rpcSecurity != RpcSecurity::TLS; + client.run(true, readOk); +} + +TEST_P(RpcTransportTest, MaliciousClient) { + auto [socketType, rpcSecurity, certificateFormat] = GetParam(); + auto server = mServers.emplace_back(std::make_unique<Server>()).get(); + ASSERT_TRUE(server->setUp()); + + Client validClient(server->getConnectToServerFn()); + ASSERT_TRUE(validClient.setUp()); + Client maliciousClient(server->getConnectToServerFn()); + ASSERT_TRUE(maliciousClient.setUp()); + + ASSERT_EQ(OK, trust(&validClient, server)); + ASSERT_EQ(OK, trust(&maliciousClient, server)); + + server->start(); + + // See UntrustedClient. + bool readOk = rpcSecurity != RpcSecurity::TLS; + maliciousClient.run(true, readOk); +} + +std::vector<RpcCertificateFormat> testRpcCertificateFormats() { + return { + RpcCertificateFormat::PEM, + RpcCertificateFormat::DER, + }; +} + +INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, + ::testing::Combine(::testing::ValuesIn(testSocketTypes(false)), + ::testing::ValuesIn(RpcSecurityValues()), + ::testing::ValuesIn(testRpcCertificateFormats())), + RpcTransportTest::PrintParamInfo); + } // namespace android int main(int argc, char** argv) { |