b3bf30bb1f
Increment the rate limiting counter when the application sends an abort message. Bug: 138655142 Test: Ran keystore_unit_tests and manually checked behavior of keystore application with confimrationui. Merged-In: I5f3af166391a32748a26f7709d30a5ac718499c0 Change-Id: I5f3af166391a32748a26f7709d30a5ac718499c0
134 lines
4 KiB
C++
134 lines
4 KiB
C++
/*
|
|
**
|
|
** Copyright 2018, 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.
|
|
*/
|
|
|
|
#ifndef KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
|
|
#define KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
|
|
|
|
#include <android/hardware/confirmationui/1.0/types.h>
|
|
#include <chrono>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
|
|
namespace keystore {
|
|
|
|
using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode;
|
|
|
|
using std::chrono::duration;
|
|
using std::chrono::time_point;
|
|
|
|
template <typename Clock = std::chrono::steady_clock> class RateLimiting {
|
|
private:
|
|
struct Slot {
|
|
Slot() : previous_start{}, prompt_start{}, counter(0) {}
|
|
typename Clock::time_point previous_start;
|
|
typename Clock::time_point prompt_start;
|
|
uint32_t counter;
|
|
};
|
|
|
|
std::unordered_map<uid_t, Slot> slots_;
|
|
|
|
uint_t latest_requester_;
|
|
|
|
static std::chrono::seconds getBackoff(uint32_t counter) {
|
|
using namespace std::chrono_literals;
|
|
switch (counter) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
return 0s;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
return 30s;
|
|
default:
|
|
return 60s * (1ULL << (counter - 6));
|
|
}
|
|
}
|
|
|
|
public:
|
|
// Exposes the number of used slots. This is only used by the test to verify the assumption
|
|
// about used counter slots.
|
|
size_t usedSlots() const { return slots_.size(); }
|
|
void doGC() {
|
|
using namespace std::chrono_literals;
|
|
using std::chrono::system_clock;
|
|
using std::chrono::time_point_cast;
|
|
auto then = Clock::now() - 24h;
|
|
auto iter = slots_.begin();
|
|
while (iter != slots_.end()) {
|
|
if (iter->second.prompt_start <= then) {
|
|
iter = slots_.erase(iter);
|
|
} else {
|
|
++iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool tryPrompt(uid_t id) {
|
|
using namespace std::chrono_literals;
|
|
// remove slots that have not been touched in 24 hours
|
|
doGC();
|
|
auto& slot = slots_[id];
|
|
auto now = Clock::now();
|
|
if (!slot.counter || slot.prompt_start <= now - getBackoff(slot.counter)) {
|
|
latest_requester_ = id;
|
|
slot.counter += 1;
|
|
slot.previous_start = slot.prompt_start;
|
|
slot.prompt_start = now;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// The app is penalized for cancelling a request. Request are rolled back only if
|
|
// the prompt was cancelled by the system: e.g. a system error or asynchronous event.
|
|
// When the user cancels the prompt, it is subject to rate limiting.
|
|
static constexpr const uint_t kInvalidRequester = -1;
|
|
|
|
void cancelPrompt() { latest_requester_ = kInvalidRequester; }
|
|
|
|
void processResult(ConfirmationResponseCode rc) {
|
|
if (latest_requester_ == kInvalidRequester) {
|
|
return;
|
|
}
|
|
switch (rc) {
|
|
case ConfirmationResponseCode::OK:
|
|
// reset the counter slot
|
|
slots_.erase(latest_requester_);
|
|
return;
|
|
case ConfirmationResponseCode::Canceled:
|
|
// nothing to do here
|
|
return;
|
|
default:;
|
|
}
|
|
|
|
// roll back latest request
|
|
auto& slot = slots_[latest_requester_];
|
|
if (slot.counter <= 1) {
|
|
slots_.erase(latest_requester_);
|
|
return;
|
|
}
|
|
slot.counter -= 1;
|
|
slot.prompt_start = slot.previous_start;
|
|
}
|
|
};
|
|
|
|
} // namespace keystore
|
|
|
|
#endif // KEYSTORE_CONFIRMATIONUI_RATE_LIMITING_H_
|