diff options
| author | 2020-02-21 21:50:25 +0000 | |
|---|---|---|
| committer | 2020-02-21 21:50:25 +0000 | |
| commit | 219643eebd19e550100f317ea52854bde4047d8f (patch) | |
| tree | febae4c820ed437fc9decca42a3494aed68b5f64 | |
| parent | 818ac485917cf803ec72ae27979f06dea26dae4b (diff) | |
| parent | d31e6edb8f512fd99229ba3249004d94e04373b4 (diff) | |
Merge "Add adbd tls APIs to adbd_auth library." am: 61addf0da3 am: e76a58ffcc am: d31e6edb8f
Change-Id: Ie91c81a5f375ca626a26e15be801a1cdd2bdd9e5
| -rw-r--r-- | libs/adbd_auth/Android.bp | 2 | ||||
| -rw-r--r-- | libs/adbd_auth/adbd_auth.cpp | 206 | ||||
| -rw-r--r-- | libs/adbd_auth/include/adbd_auth.h | 159 | ||||
| -rw-r--r-- | libs/adbd_auth/libadbd_auth.map.txt | 19 |
4 files changed, 312 insertions, 74 deletions
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp index 8ac044c091..8883c0478a 100644 --- a/libs/adbd_auth/Android.bp +++ b/libs/adbd_auth/Android.bp @@ -27,7 +27,7 @@ cc_library { version_script: "libadbd_auth.map.txt", stubs: { - versions: ["1"], + versions: ["30"], symbol_file: "libadbd_auth.map.txt", }, diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index a9c23110c9..5a0d3f6168 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -43,6 +43,8 @@ using android::base::unique_fd; +static constexpr uint32_t kAuthVersion = 1; + struct AdbdAuthPacketAuthenticated { std::string public_key; }; @@ -55,8 +57,21 @@ struct AdbdAuthPacketRequestAuthorization { std::string public_key; }; -using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected, - AdbdAuthPacketRequestAuthorization>; +struct AdbdPacketTlsDeviceConnected { + uint8_t transport_type; + std::string public_key; +}; + +struct AdbdPacketTlsDeviceDisconnected { + uint8_t transport_type; + std::string public_key; +}; + +using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, + AdbdAuthPacketDisconnected, + AdbdAuthPacketRequestAuthorization, + AdbdPacketTlsDeviceConnected, + AdbdPacketTlsDeviceDisconnected>; struct AdbdAuthContext { static constexpr uint64_t kEpollConstSocket = 0; @@ -65,6 +80,7 @@ struct AdbdAuthContext { public: explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) { + InitFrameworkHandlers(); epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC)); if (epoll_fd_ == -1) { PLOG(FATAL) << "failed to create epoll fd"; @@ -163,36 +179,58 @@ public: } } - void HandlePacket(std::string_view packet) REQUIRES(mutex_) { + void HandlePacket(std::string_view packet) EXCLUDES(mutex_) { LOG(INFO) << "received packet: " << packet; - if (packet.length() < 2) { - LOG(ERROR) << "received packet of invalid length"; - ReplaceFrameworkFd(unique_fd()); + if (packet.size() < 2) { + LOG(ERROR) << "received packet of invalid length"; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(unique_fd()); } - if (packet[0] == 'O' && packet[1] == 'K') { - CHECK(this->dispatched_prompt_.has_value()); - auto& [id, key, arg] = *this->dispatched_prompt_; - keys_.emplace(id, std::move(key)); - - this->callbacks_.key_authorized(arg, id); - this->dispatched_prompt_ = std::nullopt; - - // We need to dispatch pending prompts here upon success as well, - // since we might have multiple queued prompts. - DispatchPendingPrompt(); - } else if (packet[0] == 'N' && packet[1] == 'O') { - CHECK_EQ(2UL, packet.length()); - // TODO: Do we want a callback if the key is denied? - this->dispatched_prompt_ = std::nullopt; - DispatchPendingPrompt(); - } else { - LOG(ERROR) << "unhandled packet: " << packet; - ReplaceFrameworkFd(unique_fd()); + bool handled_packet = false; + for (size_t i = 0; i < framework_handlers_.size(); ++i) { + if (android::base::ConsumePrefix(&packet, framework_handlers_[i].code)) { + framework_handlers_[i].cb(packet); + handled_packet = true; + break; + } + } + if (!handled_packet) { + LOG(ERROR) << "unhandled packet: " << packet; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(unique_fd()); } } + void AllowUsbDevice(std::string_view buf) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + CHECK(buf.empty()); + CHECK(dispatched_prompt_.has_value()); + auto& [id, key, arg] = *dispatched_prompt_; + keys_.emplace(id, std::move(key)); + + callbacks_.key_authorized(arg, id); + dispatched_prompt_ = std::nullopt; + + // We need to dispatch pending prompts here upon success as well, + // since we might have multiple queued prompts. + DispatchPendingPrompt(); + } + + void DenyUsbDevice(std::string_view buf) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + CHECK(buf.empty()); + // TODO: Do we want a callback if the key is denied? + dispatched_prompt_ = std::nullopt; + DispatchPendingPrompt(); + } + + void KeyRemoved(std::string_view buf) EXCLUDES(mutex_) { + CHECK(!buf.empty()); + callbacks_.key_removed(buf.data(), buf.size()); + } + bool SendPacket() REQUIRES(mutex_) { if (output_queue_.empty()) { return false; @@ -201,7 +239,8 @@ public: CHECK_NE(-1, framework_fd_.get()); auto& packet = output_queue_.front(); - struct iovec iovs[2]; + struct iovec iovs[3]; + int iovcnt = 2; if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) { iovs[0].iov_base = const_cast<char*>("CK"); iovs[0].iov_len = 2; @@ -217,13 +256,29 @@ public: iovs[0].iov_len = 2; iovs[1].iov_base = p->public_key.data(); iovs[1].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdPacketTlsDeviceConnected>(&packet)) { + iovcnt = 3; + iovs[0].iov_base = const_cast<char*>("WE"); + iovs[0].iov_len = 2; + iovs[1].iov_base = &p->transport_type; + iovs[1].iov_len = 1; + iovs[2].iov_base = p->public_key.data(); + iovs[2].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdPacketTlsDeviceDisconnected>(&packet)) { + iovcnt = 3; + iovs[0].iov_base = const_cast<char*>("WF"); + iovs[0].iov_len = 2; + iovs[1].iov_base = &p->transport_type; + iovs[1].iov_len = 1; + iovs[2].iov_base = p->public_key.data(); + iovs[2].iov_len = p->public_key.size(); } else { LOG(FATAL) << "unhandled packet type?"; } output_queue_.pop_front(); - ssize_t rc = writev(framework_fd_.get(), iovs, 2); + ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt); if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { PLOG(ERROR) << "failed to write to framework fd"; ReplaceFrameworkFd(unique_fd()); @@ -308,7 +363,6 @@ public: std::lock_guard<std::mutex> lock(mutex_); ReplaceFrameworkFd(unique_fd()); } else { - std::lock_guard<std::mutex> lock(mutex_); HandlePacket(std::string_view(buf, rc)); } } @@ -329,7 +383,7 @@ public: } static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"}; - void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) { + void IteratePublicKeys(bool (*callback)(void*, const char*, size_t), void* opaque) { for (const auto& path : key_paths) { if (access(path, R_OK) == 0) { LOG(INFO) << "Loading keys from " << path; @@ -339,7 +393,7 @@ public: continue; } for (const auto& line : android::base::Split(content, "\n")) { - if (!callback(line.data(), line.size(), arg)) { + if (!callback(opaque, line.data(), line.size())) { return; } } @@ -361,7 +415,7 @@ public: std::lock_guard<std::mutex> lock(mutex_); keys_.emplace(id, public_key); output_queue_.emplace_back( - AdbdAuthPacketDisconnected{.public_key = std::string(public_key)}); + AdbdAuthPacketAuthenticated{.public_key = std::string(public_key)}); return id; } @@ -376,6 +430,32 @@ public: keys_.erase(it); } + uint64_t NotifyTlsDeviceConnected(AdbTransportType type, + std::string_view public_key) EXCLUDES(mutex_) { + uint64_t id = NextId(); + std::lock_guard<std::mutex> lock(mutex_); + keys_.emplace(id, public_key); + output_queue_.emplace_back(AdbdPacketTlsDeviceConnected{ + .transport_type = static_cast<uint8_t>(type), + .public_key = std::string(public_key)}); + Interrupt(); + return id; + } + + void NotifyTlsDeviceDisconnected(AdbTransportType type, uint64_t id) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + auto it = keys_.find(id); + if (it == keys_.end()) { + LOG(DEBUG) << "couldn't find public key to notify disconnection of tls device, skipping"; + return; + } + output_queue_.emplace_back(AdbdPacketTlsDeviceDisconnected{ + .transport_type = static_cast<uint8_t>(type), + .public_key = std::move(it->second)}); + keys_.erase(it); + Interrupt(); + } + // Interrupt the worker thread to do some work. void Interrupt() { uint64_t value = 1; @@ -387,6 +467,24 @@ public: } } + void InitFrameworkHandlers() { + // Framework wants to disconnect from a secured wifi device + framework_handlers_.emplace_back( + FrameworkPktHandler{ + .code = "DD", + .cb = std::bind(&AdbdAuthContext::KeyRemoved, this, std::placeholders::_1)}); + // Framework allows USB debugging for the device + framework_handlers_.emplace_back( + FrameworkPktHandler{ + .code = "OK", + .cb = std::bind(&AdbdAuthContext::AllowUsbDevice, this, std::placeholders::_1)}); + // Framework denies USB debugging for the device + framework_handlers_.emplace_back( + FrameworkPktHandler{ + .code = "NO", + .cb = std::bind(&AdbdAuthContext::DenyUsbDevice, this, std::placeholders::_1)}); + } + unique_fd epoll_fd_; unique_fd event_fd_; unique_fd sock_fd_; @@ -400,19 +498,27 @@ public: // We keep two separate queues: one to handle backpressure from the socket (output_queue_) // and one to make sure we only dispatch one authrequest at a time (pending_prompts_). - std::deque<AdbdAuthPacket> output_queue_; + std::deque<AdbdAuthPacket> output_queue_ GUARDED_BY(mutex_); std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_); std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_); + + // This is a list of commands that the framework could send to us. + using FrameworkHandlerCb = std::function<void(std::string_view)>; + struct FrameworkPktHandler { + const char* code; + FrameworkHandlerCb cb; + }; + std::vector<FrameworkPktHandler> framework_handlers_; }; AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) { - if (callbacks->version != 1) { + if (callbacks->version == 1) { + return new AdbdAuthContext(reinterpret_cast<AdbdAuthCallbacksV1*>(callbacks)); + } else { LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version; return nullptr; } - - return new AdbdAuthContext(&callbacks->callbacks.v1); } void adbd_auth_delete(AdbdAuthContext* ctx) { @@ -424,9 +530,9 @@ void adbd_auth_run(AdbdAuthContext* ctx) { } void adbd_auth_get_public_keys(AdbdAuthContext* ctx, - bool (*callback)(const char* public_key, size_t len, void* arg), - void* arg) { - ctx->IteratePublicKeys(callback, arg); + bool (*callback)(void* opaque, const char* public_key, size_t len), + void* opaque) { + ctx->IteratePublicKeys(callback, opaque); } uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) { @@ -438,10 +544,28 @@ void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) { } void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, - void* arg) { - ctx->PromptUser(std::string_view(public_key, len), arg); + void* opaque) { + ctx->PromptUser(std::string_view(public_key, len), opaque); +} + +uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx, + AdbTransportType type, + const char* public_key, + size_t len) { + return ctx->NotifyTlsDeviceConnected(type, std::string_view(public_key, len)); +} + +void adbd_auth_tls_device_disconnected(AdbdAuthContext* ctx, + AdbTransportType type, + uint64_t id) { + ctx->NotifyTlsDeviceDisconnected(type, id); +} + +uint32_t adbd_auth_get_max_version() { + return kAuthVersion; } -bool adbd_auth_supports_feature(AdbdAuthFeature) { +bool adbd_auth_supports_feature(AdbdAuthFeature f) { + UNUSED(f); return false; } diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h index b7c1cb88cc..6ee3166e3a 100644 --- a/libs/adbd_auth/include/adbd_auth.h +++ b/libs/adbd_auth/include/adbd_auth.h @@ -18,48 +18,159 @@ #include <stdbool.h> #include <stdint.h> +#include <sys/cdefs.h> #include <sys/types.h> -extern "C" { +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif -struct AdbdAuthCallbacksV1 { - // Callback for a successful user authorization. - void (*key_authorized)(void* arg, uint64_t id); +__BEGIN_DECLS +#if !defined(__ANDROID__) || __ANDROID_API__ >= 30 + +// The transport type of the device connection. +enum AdbTransportType : int32_t { + kAdbTransportTypeUsb = 0, + kAdbTransportTypeWifi, }; +static_assert(sizeof(AdbTransportType) == sizeof(int32_t), "Unexpected AdbTransportType size"); struct AdbdAuthCallbacks { uint32_t version; - union { - AdbdAuthCallbacksV1 v1; - } callbacks; +}; + +struct AdbdAuthCallbacksV1 : AdbdAuthCallbacks { + // Callback for a successful user authorization. + void (*key_authorized)(void* opaque, uint64_t id); + // The framework removed the key from the keystore. This callback notifies + // adbd so it can take the appropriate actions (e.g. disconnect all devices + // using that key). + void (*key_removed)(const char* public_key, size_t length); }; struct AdbdAuthContext; +typedef struct AdbdAuthContext AdbdAuthContext; -AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks); -void adbd_auth_delete(AdbdAuthContext* ctx); +/** + * Creates a new AdbdAuthContext. + * + * @param callbacks a set of user-provided callbacks used internally (see + * #AdbdAuthCallbacksV1 + * @return a new AdbdAuthContext instance. Caller is responsible for destroying + * the context with #adbd_auth_delete. + */ +AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) __INTRODUCED_IN(30); -void adbd_auth_run(AdbdAuthContext* ctx); +/** + * Destroys the AdbdAuthContext. + * + * @param ctx the AdbdAuthContext to destroy. + */ +void adbd_auth_delete(AdbdAuthContext* ctx) __INTRODUCED_IN(30); -// Iterate through the list of authorized public keys. -// Return false from the callback to stop iteration. +/** + * Starts the AdbdAuthContext. + * + * The caller may want to run this on a different thread, as this + * runs indefinitely. + * + * @param ctx the AdbdAuthContext + */ +void adbd_auth_run(AdbdAuthContext* ctx) __INTRODUCED_IN(30); + +/** + * Iterate through the list of authorized public keys. + * + * @param ctx the AdbdAuthContext + * @param callback a callback which will get called for every known adb public + * key in its keystore. To stop iteration of the keys, return false in the + * callback. Otherwise, return true to continue the iteration. + * @param opaque an opaque userdata argument + */ void adbd_auth_get_public_keys(AdbdAuthContext* ctx, - bool (*callback)(const char* public_key, size_t len, void* arg), - void* arg); + bool (*callback)(void* opaque, const char* public_key, size_t len), + void* opaque) __INTRODUCED_IN(30); + +/** + * Let system_server know that a key has been successfully used for authentication. + * + * @param ctx the AdbdAuthContext + * @param public_key the RSA key that was authorized using the AUTH protocol + * @param len the length of the public_key argument + * @return an id corresponding to the new connection + */ +uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, + const char* public_key, + size_t len) __INTRODUCED_IN(30); -// Let system_server know that a key has been successfully used for authentication. -uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len); +/** + * Let system_server know that an AUTH connection has been closed. + * + * @param ctx the AdbdAuthContext + * @param id the id of the disconnected device + */ +void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, + uint64_t id) __INTRODUCED_IN(30); -// Let system_server know that a connection has been closed. -void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id); +/** + * Prompt the user to authorize a public key. + * + * When this happens, a callback will be run on the auth thread with the result. + * + * @param ctx the AdbdAuthContext + * @param public_key the RSA public key to prompt user with + * @param len the length of the public_key argument + * @param arg an opaque userdata argument + */ +void adbd_auth_prompt_user(AdbdAuthContext* ctx, + const char* public_key, + size_t len, void* opaque) __INTRODUCED_IN(30); -// Prompt the user to authorize a public key. -// When this happens, a callback will be run on the auth thread with the result. -void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg); +/** + * Let system_server know that a TLS device has connected. + * + * @param ctx the AdbdAuthContext + * @param type the transport type of the connection (see #AdbTransportType) + * @param public_key the RSA public key used to establish the connection + * @param len the length of the public_key argument + * @return an id corresponding to the new connection + */ +uint64_t adbd_auth_tls_device_connected(AdbdAuthContext* ctx, + AdbTransportType type, + const char* public_key, + size_t len) __INTRODUCED_IN(30); -enum AdbdAuthFeature { +/** + * Let system_server know that a TLS device has disconnected. + * + * @param ctx the AdbdAuthContext + * @param type the transport type of the connection (see #AdbTransportType) + * @param the id of the disconnected device (see #adbd_tls_device_connected) + */ +void adbd_auth_tls_device_disconnected(AdbdAuthContext* ctx, + AdbTransportType type, + uint64_t id) __INTRODUCED_IN(30); + +/** + * Returns the max #AdbdAuthCallbacks version. + * + * The version starts at 1, with version 1 corresponding to the + * #AdbdAuthCallbacksV1 struct. + * + * @return the max #AdbdAuthCallbacks version. + */ +uint32_t adbd_auth_get_max_version(void) __INTRODUCED_IN(30); + +enum AdbdAuthFeature : int32_t { }; -bool adbd_auth_supports_feature(AdbdAuthFeature f); +/** + * Checks if a feature is supported by the framework. See #AdbdAuthFeature. + * + * @param feature the feature to check for support + * @return true if the feature is supported + */ +bool adbd_auth_supports_feature(AdbdAuthFeature feature); -} +#endif //!__ANDROID__ || __ANDROID_API__ >= 30 +__END_DECLS diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt index d01233c960..5857ecb98e 100644 --- a/libs/adbd_auth/libadbd_auth.map.txt +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -1,13 +1,16 @@ LIBADBD_AUTH { global: - adbd_auth_new; # apex - adbd_auth_delete; # apex - adbd_auth_run; # apex - adbd_auth_get_public_keys; #apex - adbd_auth_notify_auth; # apex - adbd_auth_notify_disconnect; # apex - adbd_auth_prompt_user; # apex - adbd_auth_supports_feature; # apex + adbd_auth_new; # apex introduced=30 + adbd_auth_delete; # apex introduced=30 + adbd_auth_run; # apex introduced=30 + adbd_auth_get_public_keys; #apex introduced=30 + adbd_auth_notify_auth; # apex introduced=30 + adbd_auth_notify_disconnect; # apex introduced=30 + adbd_auth_prompt_user; # apex introduced=30 + adbd_auth_tls_device_connected; # apex introduced=30 + adbd_auth_tls_device_disconnected; # apex introduced=30 + adbd_auth_get_max_version; # apex introduced=30 + adbd_auth_supports_feature; # apex introduced=30 local: *; }; |