/* * * Copyright 2019, 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. */ #include "TrustyConfirmationUI.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace aidl::android::hardware::confirmationui { using namespace secure_input; using ::android::trusty::confirmationui::TrustyAppError; using ::teeui::AbortMsg; using ::teeui::DeliverTestCommandMessage; using ::teeui::DeliverTestCommandResponse; using ::teeui::FetchConfirmationResult; using ::teeui::MsgString; using ::teeui::MsgVector; using ::teeui::PromptUserConfirmationMsg; using ::teeui::PromptUserConfirmationResponse; using ::teeui::ResultMsg; using ::secure_input::createSecureInput; using ::std::tie; using TeeuiRc = ::teeui::ResponseCode; constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0"; constexpr const char kConfirmationuiAppName[] = CONFIRMATIONUI_PORT; namespace { class Finalize { private: std::function f_; public: Finalize(std::function f) : f_(f) {} ~Finalize() { if (f_) f_(); } void release() { f_ = {}; } }; int convertRc(TeeuiRc trc) { static_assert( uint32_t(TeeuiRc::OK) == uint32_t(IConfirmationUI::OK) && uint32_t(TeeuiRc::Canceled) == uint32_t(IConfirmationUI::CANCELED) && uint32_t(TeeuiRc::Aborted) == uint32_t(IConfirmationUI::ABORTED) && uint32_t(TeeuiRc::OperationPending) == uint32_t(IConfirmationUI::OPERATION_PENDING) && uint32_t(TeeuiRc::Ignored) == uint32_t(IConfirmationUI::IGNORED) && uint32_t(TeeuiRc::SystemError) == uint32_t(IConfirmationUI::SYSTEM_ERROR) && uint32_t(TeeuiRc::Unimplemented) == uint32_t(IConfirmationUI::UNIMPLEMENTED) && uint32_t(TeeuiRc::Unexpected) == uint32_t(IConfirmationUI::UNEXPECTED) && uint32_t(TeeuiRc::UIError) == uint32_t(IConfirmationUI::UI_ERROR) && uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(IConfirmationUI::UI_ERROR_MISSING_GLYPH) && uint32_t(TeeuiRc::UIErrorMessageTooLong) == uint32_t(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG) && uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) == uint32_t(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING), "teeui::ResponseCode and " "::android::hardware::confirmationui::V1_0::Responsecude are out of " "sync"); return static_cast(trc); } teeui::UIOption convertUIOption(UIOption uio) { static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) == uint32_t(teeui::UIOption::AccessibilityInverted) && uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) == uint32_t(teeui::UIOption::AccessibilityMagnified), "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption " "are out of sync"); return teeui::UIOption(uio); } inline MsgString stdString2MsgString(const string& s) { return {s.c_str(), s.c_str() + s.size()}; } template inline MsgVector stdVector2MsgVector(const vector& v) { return {v}; } inline MsgVector stdVector2MsgVector(const vector& v) { MsgVector result(v.size()); for (unsigned int i = 0; i < v.size(); ++i) { result[i] = convertUIOption(v[i]); } return result; } } // namespace TrustyConfirmationUI::TrustyConfirmationUI() : listener_state_(ListenerState::None), prompt_result_(IConfirmationUI::IGNORED) {} TrustyConfirmationUI::~TrustyConfirmationUI() { ListenerState state = listener_state_; if (state == ListenerState::SetupDone || state == ListenerState::Interactive) { abort(); } if (state != ListenerState::None) { callback_thread_.join(); } } std::tuple, MsgVector> TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText, const MsgVector& extraData, const MsgString& locale, const MsgVector& uiOptions) { std::unique_lock stateLock(listener_state_lock_); /* * This is the main listener thread function. The listener thread life cycle * is equivalent to the life cycle of a single confirmation request. The life * cycle is devided in four phases. * * The starting phase: * * The Trusted App gets loaded and/or the connection to it gets established. * * A connection to the secure input device is established. * * The prompt is initiated. This sends all information required by the * confirmation dialog to the TA. The dialog is not yet displayed. * * An event loop is created. * * The event loop listens for user input events, fetches them from the * secure input device, and delivers them to the TA. * * All evdev devices are grabbed to give confirmationui exclusive access * to user input. * * Note: During the starting phase the hwbinder service thread is blocked and * waiting for possible Errors. If the setup phase concludes sucessfully, the * hwbinder service thread gets unblocked and returns successfully. Errors * that occur after the first phase are delivered by callback interface. * * * The 2nd phase - non interactive phase * * The event loop thread is started. * * After a grace period: * * A handshake between the secure input device SecureInput and the TA * is performed. * * The input event handler are armed to process user input events. * * * The 3rd phase - interactive phase * * We wait to any external event * * Abort * * Secure user input asserted * * Secure input delivered (for non interactive VTS testing) * * The result is fetched from the TA. * * * The 4th phase - cleanup * The cleanup phase is given by the scope of automatic variables created * in this function. The cleanup commences in reverse order of their creation. * Here is a list of more complex items in the order in which they go out of * scope * * finalizeSecureTouch - signals and joins the secure touch thread. * * eventloop - signals and joins the event loop thread. The event * handlers also own all EventDev instances which ungrab the event devices. * When the eventloop goes out of scope the EventDevs get destroyed * relinquishing the exclusive hold on the event devices. * * finalizeConfirmationPrompt - calls abort on the TA, making sure a * pending operation gets canceled. If the prompt concluded successfully this * is a spurious call but semantically a no op. * * secureInput - shuts down the connection to the secure input device * SecureInput. * * app - disconnects the TA. Since app is a shared pointer this may not * unload the app here. It is possible that more instances of the shared * pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and * TrustyConfirmationUI::abort. But these instances are extremely short lived * and it is safe if they are destroyed by either. * * stateLock - unlocks the listener_state_lock_ if it happens to be held * at the time of return. */ std::tuple, MsgVector> result; TeeuiRc& rc = std::get(result); rc = TeeuiRc::SystemError; listener_state_ = ListenerState::Starting; auto app = std::make_shared(kTrustyDeviceName, kConfirmationuiAppName); if (!app) return result; // TeeuiRc::SystemError app_ = app; auto hsBegin = [&]() -> std::tuple { auto [error, result] = app->issueCmd(); auto& [rc, nCo] = result; if (error != TrustyAppError::OK || rc != TeeuiRc::OK) { LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/" << uint32_t(rc) << ")"; rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc; } return result; }; auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc { auto [error, finalizeResponse] = app->issueCmd( nCi, sig); auto& [rc] = finalizeResponse; if (error != TrustyAppError::OK || rc != TeeuiRc::OK) { LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/" << uint32_t(rc) << ")"; rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc; } return rc; }; auto deliverInput = [&](DTupKeyEvent event, const Signature& sig) -> std::tuple { auto [error, result] = app->issueCmd(event, sig); auto& [rc, ir] = result; if (error != TrustyAppError::OK) { LOG(ERROR) << "Failed to deliver input command"; rc = TeeuiRc::SystemError; } return result; }; std::atomic eventRC = TeeuiRc::OperationPending; auto inputResult = [&](TeeuiRc rc) { TeeuiRc expected = TeeuiRc::OperationPending; if (eventRC.compare_exchange_strong(expected, rc)) { listener_state_condv_.notify_all(); } }; // create Secure Input device. auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult); if (!secureInput || !(*secureInput)) { LOG(ERROR) << "Failed to open secure input device"; return result; // TeeuiRc::SystemError; } Finalize finalizeConfirmationPrompt([app] { LOG(INFO) << "Calling abort for cleanup"; app->issueCmd(); }); // initiate prompt LOG(INFO) << "Initiating prompt"; TrustyAppError error; auto initResponse = std::tie(rc); std::tie(error, initResponse) = app->issueCmd( promptText, extraData, locale, uiOptions); if (error == TrustyAppError::MSG_TOO_LONG) { LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long"; rc = TeeuiRc::UIErrorMessageTooLong; return result; } else if (error != TrustyAppError::OK) { LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error); return result; // TeeuiRc::SystemError; } if (rc != TeeuiRc::OK) { LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc); return result; } LOG(INFO) << "Grabbing event devices"; EventLoop eventloop; bool grabbed = grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) { if (!(flags & POLLIN)) return; secureInput->handleEvent(evDev); }); if (!grabbed) { rc = TeeuiRc::SystemError; return result; } abort_called_ = false; secureInputDelivered_ = false; // ############################## Start 2nd Phase ############################################# listener_state_ = ListenerState::SetupDone; stateLock.unlock(); listener_state_condv_.notify_all(); if (!eventloop.start()) { rc = TeeuiRc::SystemError; return result; } stateLock.lock(); LOG(INFO) << "going to sleep for the grace period"; auto then = std::chrono::system_clock::now() + std::chrono::milliseconds(kUserPreInputGracePeriodMillis) + std::chrono::microseconds(50); listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; }); LOG(INFO) << "waking up"; if (abort_called_) { LOG(ERROR) << "Abort called"; result = {TeeuiRc::Aborted, {}, {}}; return result; } LOG(INFO) << "Arming event poller"; // tell the event poller to act on received input events from now on. secureInput->start(); // ############################## Start 3rd Phase - interactive phase ######################### LOG(INFO) << "Transition to Interactive"; listener_state_ = ListenerState::Interactive; stateLock.unlock(); listener_state_condv_.notify_all(); stateLock.lock(); listener_state_condv_.wait(stateLock, [&]() { return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_; }); LOG(INFO) << "Listener waking up"; if (abort_called_) { LOG(ERROR) << "Abort called"; result = {TeeuiRc::Aborted, {}, {}}; return result; } if (!secureInputDelivered_) { if (eventRC != TeeuiRc::OK) { LOG(ERROR) << "Bad input response"; result = {eventRC, {}, {}}; return result; } } stateLock.unlock(); LOG(INFO) << "Fetching Result"; std::tie(error, result) = app->issueCmd(); LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc); if (error != TrustyAppError::OK) { result = {TeeuiRc::SystemError, {}, {}}; } return result; // ############################## Start 4th Phase - cleanup ################################## } // Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI // follow. ::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation( const shared_ptr& resultCB, const vector& promptTextBytes, const vector& extraData, const string& locale, const vector& uiOptions) { std::unique_lock stateLock(listener_state_lock_, std::defer_lock); string promptText(promptTextBytes.begin(), promptTextBytes.end()); if (!stateLock.try_lock()) { return ndk::ScopedAStatus( AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING)); } switch (listener_state_) { case ListenerState::None: break; case ListenerState::Starting: case ListenerState::SetupDone: case ListenerState::Interactive: return ndk::ScopedAStatus( AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING)); case ListenerState::Terminating: callback_thread_.join(); listener_state_ = ListenerState::None; break; default: return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED)); } assert(listener_state_ == ListenerState::None); callback_thread_ = std::thread( [this](const shared_ptr& resultCB, const string& promptText, const vector& extraData, const string& locale, const vector& uiOptions) { auto [trc, msg, token] = promptUserConfirmation_( stdString2MsgString(promptText), stdVector2MsgVector(extraData), stdString2MsgString(locale), stdVector2MsgVector(uiOptions)); bool do_callback = (listener_state_ == ListenerState::Interactive || listener_state_ == ListenerState::SetupDone) && resultCB; prompt_result_ = convertRc(trc); listener_state_ = ListenerState::Terminating; if (do_callback) { auto error = resultCB->result(prompt_result_, msg, token); if (!error.isOk()) { LOG(ERROR) << "Result callback failed " << error.getDescription(); } } else { listener_state_condv_.notify_all(); } }, resultCB, promptText, extraData, locale, uiOptions); listener_state_condv_.wait(stateLock, [this] { return listener_state_ == ListenerState::SetupDone || listener_state_ == ListenerState::Interactive || listener_state_ == ListenerState::Terminating; }); if (listener_state_ == ListenerState::Terminating) { callback_thread_.join(); listener_state_ = ListenerState::None; return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_)); } return ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) { int rc = IConfirmationUI::IGNORED; { /* * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct * implementation responds with a mock confirmation token signed with a test key. The * problem is that the non interactive grace period was not formalized in the HAL spec, * so that the VTS test does not account for the grace period. (It probably should.) * This means we can only pass the VTS test if we block until the grace period is over * (SetupDone -> Interactive) before we deliver the input event. * * The true secure input is delivered by a different mechanism and gets ignored - * not queued - until the grace period is over. * */ std::unique_lock stateLock(listener_state_lock_); listener_state_condv_.wait(stateLock, [this] { return listener_state_ != ListenerState::SetupDone; }); if (listener_state_ != ListenerState::Interactive) return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED)); auto sapp = app_.lock(); if (!sapp) return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED)); auto [error, response] = sapp->issueCmd( static_cast(secureInputToken.challenge)); if (error != TrustyAppError::OK) return ndk::ScopedAStatus( AStatus_fromServiceSpecificError(IConfirmationUI::SYSTEM_ERROR)); auto& [trc] = response; if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true; rc = convertRc(trc); } if (secureInputDelivered_) listener_state_condv_.notify_all(); // VTS test expect an OK response if the event was successfully delivered. // But since the TA returns the callback response now, we have to translate // Canceled into OK. Canceled is only returned if the delivered event canceled // the operation, which means that the event was successfully delivered. Thus // we return OK. if (rc == IConfirmationUI::CANCELED) return ndk::ScopedAStatus::ok(); if (rc != IConfirmationUI::OK) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc)); } return ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus TrustyConfirmationUI::abort() { { std::unique_lock stateLock(listener_state_lock_); if (listener_state_ == ListenerState::SetupDone || listener_state_ == ListenerState::Interactive) { auto sapp = app_.lock(); if (sapp) sapp->issueCmd(); abort_called_ = true; } } listener_state_condv_.notify_all(); return ndk::ScopedAStatus::ok(); } std::shared_ptr createTrustyConfirmationUI() { return ndk::SharedRefBase::make(); } } // namespace aidl::android::hardware::confirmationui