Fix security risk in pairing process w/ add device name for OTP pair

This commit is contained in:
Yukino Song
2024-08-28 23:52:02 +08:00
parent 096e8ac62f
commit 36b1a15676
4 changed files with 37 additions and 17 deletions
+9 -1
View File
@@ -765,7 +765,15 @@ namespace confighttp {
throw std::runtime_error("Passphrase too short!"); throw std::runtime_error("Passphrase too short!");
} }
outputTree.put("otp", nvhttp::request_otp(it->second)); std::string passphrase = it->second;
std::string deviceName;
it = args.find("deviceName");
if (it != std::end(args)) {
deviceName = it->second;
}
outputTree.put("otp", nvhttp::request_otp(passphrase, deviceName));
outputTree.put("status", true); outputTree.put("status", true);
outputTree.put("message", "OTP created, effective within 3 minutes."); outputTree.put("message", "OTP created, effective within 3 minutes.");
} }
+22 -12
View File
@@ -49,6 +49,7 @@ namespace nvhttp {
crypto::cert_chain_t cert_chain; crypto::cert_chain_t cert_chain;
static std::string one_time_pin; static std::string one_time_pin;
static std::string otp_passphrase; static std::string otp_passphrase;
static std::string otp_device_name;
static std::chrono::time_point<std::chrono::steady_clock> otp_creation_time; static std::chrono::time_point<std::chrono::steady_clock> otp_creation_time;
class SunshineHTTPS: public SimpleWeb::HTTPS { class SunshineHTTPS: public SimpleWeb::HTTPS {
@@ -500,6 +501,13 @@ namespace nvhttp {
auto it = map_id_sess.find(client.uniqueID); auto it = map_id_sess.find(client.uniqueID);
// set up named cert
named_cert_t named_cert;
named_cert.name = client.deviceName;
named_cert.cert = client.cert;
named_cert.uuid = uuid_util::uuid_t::generate().string();
client_root.named_devices.emplace_back(named_cert);
update_id_client(client.uniqueID, std::move(client.cert), op_e::ADD); update_id_client(client.uniqueID, std::move(client.cert), op_e::ADD);
map_id_sess.erase(it); map_id_sess.erase(it);
} }
@@ -616,17 +624,22 @@ namespace nvhttp {
if (one_time_pin.empty() || (std::chrono::steady_clock::now() - otp_creation_time > OTP_EXPIRE_DURATION)) { if (one_time_pin.empty() || (std::chrono::steady_clock::now() - otp_creation_time > OTP_EXPIRE_DURATION)) {
one_time_pin.clear(); one_time_pin.clear();
otp_passphrase.clear(); otp_passphrase.clear();
otp_device_name.clear();
tree.put("root.<xmlattr>.status_code", 503); tree.put("root.<xmlattr>.status_code", 503);
tree.put("root.<xmlattr>.status_message", "OTP auth not available."); tree.put("root.<xmlattr>.status_message", "OTP auth not available.");
} else { } else {
auto hash = util::hex(crypto::hash(one_time_pin + ptr->second.async_insert_pin.salt + otp_passphrase), true); auto hash = util::hex(crypto::hash(one_time_pin + ptr->second.async_insert_pin.salt + otp_passphrase), true);
if (hash.to_string_view() == it->second) { if (hash.to_string_view() == it->second) {
ptr->second.async_insert_pin.response = std::move(response); if (!otp_device_name.empty()) {
fg.disable(); ptr->second.client.deviceName = std::move(otp_device_name);
pin(one_time_pin, deviceName); }
getservercert(ptr->second, tree, one_time_pin);
one_time_pin.clear(); one_time_pin.clear();
otp_passphrase.clear(); otp_passphrase.clear();
otp_device_name.clear();
return; return;
} }
} }
@@ -701,13 +714,9 @@ namespace nvhttp {
auto &sess = std::begin(map_id_sess)->second; auto &sess = std::begin(map_id_sess)->second;
getservercert(sess, tree, pin); getservercert(sess, tree, pin);
// set up named cert if (!name.empty()) {
client_t &client = client_root; sess.client.deviceName = name;
named_cert_t named_cert; }
named_cert.name = name.empty() ? sess.client.deviceName : name;
named_cert.cert = sess.client.cert;
named_cert.uuid = uuid_util::uuid_t::generate().string();
client.named_devices.emplace_back(named_cert);
// response to the request for pin // response to the request for pin
std::ostringstream data; std::ostringstream data;
@@ -1232,13 +1241,14 @@ namespace nvhttp {
tcp.join(); tcp.join();
} }
std::string request_otp(const std::string& passphrase) { std::string request_otp(const std::string& passphrase, const std::string& deviceName) {
if (passphrase.size() < 4) { if (passphrase.size() < 4) {
return ""; return "";
} }
otp_passphrase = passphrase;
one_time_pin = crypto::rand_alphabet(4, "0123456789"sv); one_time_pin = crypto::rand_alphabet(4, "0123456789"sv);
otp_passphrase = passphrase;
otp_device_name = deviceName;
otp_creation_time = std::chrono::steady_clock::now(); otp_creation_time = std::chrono::steady_clock::now();
return one_time_pin; return one_time_pin;
+1 -1
View File
@@ -67,7 +67,7 @@ namespace nvhttp {
bool bool
pin(std::string pin, std::string name); pin(std::string pin, std::string name);
std::string request_otp(const std::string& passphrase); std::string request_otp(const std::string& passphrase, const std::string& deviceName);
/** /**
* @brief Remove single client. * @brief Remove single client.
+5 -3
View File
@@ -29,7 +29,8 @@
<form v-if="currentTab === 'OTP'" class="form d-flex flex-column align-items-center" @submit.prevent="requestOTP"> <form v-if="currentTab === 'OTP'" class="form d-flex flex-column align-items-center" @submit.prevent="requestOTP">
<div class="card flex-column d-flex p-4 mb-4"> <div class="card flex-column d-flex p-4 mb-4">
<h1 class="my-4 text-center">{{ otp && otp || '????' }}</h1> <h1 class="my-4 text-center">{{ otp && otp || '????' }}</h1>
<input type="text" pattern="[0-9a-zA-Z]{4,}" :placeholder="`${$t('pin.otp_passphrase')}`" v-model="passphrase" required autofocus class="form-control my-4" /> <input type="text" pattern="[0-9a-zA-Z]{4,}" :placeholder="`${$t('pin.otp_passphrase')}`" v-model="passphrase" required autofocus class="form-control mt-2" />
<input type="text" :placeholder="`${$t('pin.device_name')}`" v-model="deviceName" class="form-control my-4" />
<button class="btn btn-primary">{{ $t('pin.generate_pin') }}</button> <button class="btn btn-primary">{{ $t('pin.generate_pin') }}</button>
</div> </div>
<div v-if="otpMessage" class="alert" :class="['alert-' + otpStatus]">{{ otpMessage }}</div> <div v-if="otpMessage" class="alert" :class="['alert-' + otpStatus]">{{ otpMessage }}</div>
@@ -67,7 +68,8 @@
otp: '', otp: '',
passphrase: '', passphrase: '',
otpMessage: '', otpMessage: '',
otpStatus: 'warning' otpStatus: 'warning',
deviceName: ''
} }
}, },
methods: { methods: {
@@ -93,7 +95,7 @@
}); });
}, },
requestOTP() { requestOTP() {
fetch(`/api/otp?passphrase=${this.passphrase}`) fetch(`/api/otp?passphrase=${this.passphrase}${this.deviceName && `&deviceName=${this.deviceName}` || ''}`)
.then(resp => resp.json()) .then(resp => resp.json())
.then(resp => { .then(resp => {
if (resp.status !== 'true') { if (resp.status !== 'true') {