Merge pull request #6549
82d21f5
easylogging++: sanitize log payload (moneromooo-monero)7d0b7e8
[master] MMS: New 'config_checksum' subcommand (rbrunner7)
This commit is contained in:
commit
e17c864ba2
|
@ -2475,6 +2475,100 @@ void DefaultLogDispatchCallback::handle(const LogDispatchData* data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Transform>
|
||||||
|
static inline std::string utf8canonical(const std::string &s, Transform t = [](wint_t c)->wint_t { return c; })
|
||||||
|
{
|
||||||
|
std::string sc = "";
|
||||||
|
size_t avail = s.size();
|
||||||
|
const char *ptr = s.data();
|
||||||
|
wint_t cp = 0;
|
||||||
|
int bytes = 1;
|
||||||
|
char wbuf[8], *wptr;
|
||||||
|
while (avail--)
|
||||||
|
{
|
||||||
|
if ((*ptr & 0x80) == 0)
|
||||||
|
{
|
||||||
|
cp = *ptr++;
|
||||||
|
bytes = 1;
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xe0) == 0xc0)
|
||||||
|
{
|
||||||
|
if (avail < 1)
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
cp = (*ptr++ & 0x1f) << 6;
|
||||||
|
cp |= *ptr++ & 0x3f;
|
||||||
|
--avail;
|
||||||
|
bytes = 2;
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xf0) == 0xe0)
|
||||||
|
{
|
||||||
|
if (avail < 2)
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
cp = (*ptr++ & 0xf) << 12;
|
||||||
|
cp |= (*ptr++ & 0x3f) << 6;
|
||||||
|
cp |= *ptr++ & 0x3f;
|
||||||
|
avail -= 2;
|
||||||
|
bytes = 3;
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xf8) == 0xf0)
|
||||||
|
{
|
||||||
|
if (avail < 3)
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
cp = (*ptr++ & 0x7) << 18;
|
||||||
|
cp |= (*ptr++ & 0x3f) << 12;
|
||||||
|
cp |= (*ptr++ & 0x3f) << 6;
|
||||||
|
cp |= *ptr++ & 0x3f;
|
||||||
|
avail -= 3;
|
||||||
|
bytes = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
|
||||||
|
cp = t(cp);
|
||||||
|
if (cp <= 0x7f)
|
||||||
|
bytes = 1;
|
||||||
|
else if (cp <= 0x7ff)
|
||||||
|
bytes = 2;
|
||||||
|
else if (cp <= 0xffff)
|
||||||
|
bytes = 3;
|
||||||
|
else if (cp <= 0x10ffff)
|
||||||
|
bytes = 4;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid code point UTF-8 transformation");
|
||||||
|
|
||||||
|
wptr = wbuf;
|
||||||
|
switch (bytes)
|
||||||
|
{
|
||||||
|
case 1: *wptr++ = cp; break;
|
||||||
|
case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
|
||||||
|
case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
|
||||||
|
case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
|
||||||
|
default: throw std::runtime_error("Invalid UTF-8");
|
||||||
|
}
|
||||||
|
*wptr = 0;
|
||||||
|
sc.append(wbuf, bytes);
|
||||||
|
cp = 0;
|
||||||
|
bytes = 1;
|
||||||
|
}
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sanitize(std::string &s)
|
||||||
|
{
|
||||||
|
s = utf8canonical(s, [](wint_t c)->wint_t {
|
||||||
|
if (c == 9 || c == 10 || c == 13)
|
||||||
|
return c;
|
||||||
|
if (c < 0x20)
|
||||||
|
return '?';
|
||||||
|
if (c == 0x7f)
|
||||||
|
return '?';
|
||||||
|
if (c >= 0x80 && c <= 0x9f)
|
||||||
|
return '?';
|
||||||
|
return c;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, base::type::string_t&& rawLinePayload, base::type::string_t&& logLine) {
|
void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, base::type::string_t&& rawLinePayload, base::type::string_t&& logLine) {
|
||||||
if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) {
|
if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) {
|
||||||
if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
|
if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
|
||||||
|
@ -2506,6 +2600,8 @@ void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix,
|
||||||
m_data->logMessage()->logger()->logBuilder()->setColor(el::base::utils::colorFromLevel(level), false);
|
m_data->logMessage()->logger()->logBuilder()->setColor(el::base::utils::colorFromLevel(level), false);
|
||||||
ELPP_COUT << rawLinePrefix;
|
ELPP_COUT << rawLinePrefix;
|
||||||
m_data->logMessage()->logger()->logBuilder()->setColor(color == el::Color::Default ? el::base::utils::colorFromLevel(level): color, color != el::Color::Default);
|
m_data->logMessage()->logger()->logBuilder()->setColor(color == el::Color::Default ? el::base::utils::colorFromLevel(level): color, color != el::Color::Default);
|
||||||
|
try { sanitize(rawLinePayload); }
|
||||||
|
catch (const std::exception &e) { rawLinePayload = "<Invalid UTF-8 in log>"; }
|
||||||
ELPP_COUT << rawLinePayload;
|
ELPP_COUT << rawLinePayload;
|
||||||
m_data->logMessage()->logger()->logBuilder()->setColor(el::Color::Default, false);
|
m_data->logMessage()->logger()->logBuilder()->setColor(el::Color::Default, false);
|
||||||
ELPP_COUT << std::flush;
|
ELPP_COUT << std::flush;
|
||||||
|
|
|
@ -86,7 +86,8 @@ set(common_private_headers
|
||||||
updates.h
|
updates.h
|
||||||
aligned.h
|
aligned.h
|
||||||
timings.h
|
timings.h
|
||||||
combinator.h)
|
combinator.h
|
||||||
|
utf8.h)
|
||||||
|
|
||||||
monero_private_headers(common
|
monero_private_headers(common
|
||||||
${common_private_headers})
|
${common_private_headers})
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
// Copyright (c) 2019, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cwchar>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace tools
|
||||||
|
{
|
||||||
|
template<typename T, typename Transform>
|
||||||
|
inline T utf8canonical(const T &s, Transform t = [](wint_t c)->wint_t { return c; })
|
||||||
|
{
|
||||||
|
T sc = "";
|
||||||
|
size_t avail = s.size();
|
||||||
|
const char *ptr = s.data();
|
||||||
|
wint_t cp = 0;
|
||||||
|
int bytes = 1;
|
||||||
|
char wbuf[8], *wptr;
|
||||||
|
while (avail--)
|
||||||
|
{
|
||||||
|
if ((*ptr & 0x80) == 0)
|
||||||
|
{
|
||||||
|
cp = *ptr++;
|
||||||
|
bytes = 1;
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xe0) == 0xc0)
|
||||||
|
{
|
||||||
|
if (avail < 1)
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
cp = (*ptr++ & 0x1f) << 6;
|
||||||
|
cp |= *ptr++ & 0x3f;
|
||||||
|
--avail;
|
||||||
|
bytes = 2;
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xf0) == 0xe0)
|
||||||
|
{
|
||||||
|
if (avail < 2)
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
cp = (*ptr++ & 0xf) << 12;
|
||||||
|
cp |= (*ptr++ & 0x3f) << 6;
|
||||||
|
cp |= *ptr++ & 0x3f;
|
||||||
|
avail -= 2;
|
||||||
|
bytes = 3;
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xf8) == 0xf0)
|
||||||
|
{
|
||||||
|
if (avail < 3)
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
cp = (*ptr++ & 0x7) << 18;
|
||||||
|
cp |= (*ptr++ & 0x3f) << 12;
|
||||||
|
cp |= (*ptr++ & 0x3f) << 6;
|
||||||
|
cp |= *ptr++ & 0x3f;
|
||||||
|
avail -= 3;
|
||||||
|
bytes = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid UTF-8");
|
||||||
|
|
||||||
|
cp = t(cp);
|
||||||
|
if (cp <= 0x7f)
|
||||||
|
bytes = 1;
|
||||||
|
else if (cp <= 0x7ff)
|
||||||
|
bytes = 2;
|
||||||
|
else if (cp <= 0xffff)
|
||||||
|
bytes = 3;
|
||||||
|
else if (cp <= 0x10ffff)
|
||||||
|
bytes = 4;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid code point UTF-8 transformation");
|
||||||
|
|
||||||
|
wptr = wbuf;
|
||||||
|
switch (bytes)
|
||||||
|
{
|
||||||
|
case 1: *wptr++ = cp; break;
|
||||||
|
case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
|
||||||
|
case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
|
||||||
|
case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
|
||||||
|
default: throw std::runtime_error("Invalid UTF-8");
|
||||||
|
}
|
||||||
|
*wptr = 0;
|
||||||
|
sc.append(wbuf, bytes);
|
||||||
|
cp = 0;
|
||||||
|
bytes = 1;
|
||||||
|
}
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,6 +41,7 @@
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "misc_log_ex.h"
|
#include "misc_log_ex.h"
|
||||||
#include "fnv1.h"
|
#include "fnv1.h"
|
||||||
|
#include "common/utf8.h"
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \namespace Language
|
* \namespace Language
|
||||||
|
@ -73,78 +74,11 @@ namespace Language
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline T utf8canonical(const T &s)
|
|
||||||
{
|
|
||||||
T sc = "";
|
|
||||||
size_t avail = s.size();
|
|
||||||
const char *ptr = s.data();
|
|
||||||
wint_t cp = 0;
|
|
||||||
int bytes = 1;
|
|
||||||
char wbuf[8], *wptr;
|
|
||||||
while (avail--)
|
|
||||||
{
|
|
||||||
if ((*ptr & 0x80) == 0)
|
|
||||||
{
|
|
||||||
cp = *ptr++;
|
|
||||||
bytes = 1;
|
|
||||||
}
|
|
||||||
else if ((*ptr & 0xe0) == 0xc0)
|
|
||||||
{
|
|
||||||
if (avail < 1)
|
|
||||||
throw std::runtime_error("Invalid UTF-8");
|
|
||||||
cp = (*ptr++ & 0x1f) << 6;
|
|
||||||
cp |= *ptr++ & 0x3f;
|
|
||||||
--avail;
|
|
||||||
bytes = 2;
|
|
||||||
}
|
|
||||||
else if ((*ptr & 0xf0) == 0xe0)
|
|
||||||
{
|
|
||||||
if (avail < 2)
|
|
||||||
throw std::runtime_error("Invalid UTF-8");
|
|
||||||
cp = (*ptr++ & 0xf) << 12;
|
|
||||||
cp |= (*ptr++ & 0x3f) << 6;
|
|
||||||
cp |= *ptr++ & 0x3f;
|
|
||||||
avail -= 2;
|
|
||||||
bytes = 3;
|
|
||||||
}
|
|
||||||
else if ((*ptr & 0xf8) == 0xf0)
|
|
||||||
{
|
|
||||||
if (avail < 3)
|
|
||||||
throw std::runtime_error("Invalid UTF-8");
|
|
||||||
cp = (*ptr++ & 0x7) << 18;
|
|
||||||
cp |= (*ptr++ & 0x3f) << 12;
|
|
||||||
cp |= (*ptr++ & 0x3f) << 6;
|
|
||||||
cp |= *ptr++ & 0x3f;
|
|
||||||
avail -= 3;
|
|
||||||
bytes = 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Invalid UTF-8");
|
|
||||||
|
|
||||||
cp = std::towlower(cp);
|
|
||||||
wptr = wbuf;
|
|
||||||
switch (bytes)
|
|
||||||
{
|
|
||||||
case 1: *wptr++ = cp; break;
|
|
||||||
case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
|
|
||||||
case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
|
|
||||||
case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
|
|
||||||
default: throw std::runtime_error("Invalid UTF-8");
|
|
||||||
}
|
|
||||||
*wptr = 0;
|
|
||||||
sc += T(wbuf, bytes);
|
|
||||||
cp = 0;
|
|
||||||
bytes = 1;
|
|
||||||
}
|
|
||||||
return sc;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WordHash
|
struct WordHash
|
||||||
{
|
{
|
||||||
std::size_t operator()(const epee::wipeable_string &s) const
|
std::size_t operator()(const epee::wipeable_string &s) const
|
||||||
{
|
{
|
||||||
const epee::wipeable_string sc = utf8canonical(s);
|
const epee::wipeable_string sc = tools::utf8canonical(s, [](wint_t c) -> wint_t { return std::towlower(c); });
|
||||||
return epee::fnv::FNV1a(sc.data(), sc.size());
|
return epee::fnv::FNV1a(sc.data(), sc.size());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -153,8 +87,8 @@ namespace Language
|
||||||
{
|
{
|
||||||
bool operator()(const epee::wipeable_string &s0, const epee::wipeable_string &s1) const
|
bool operator()(const epee::wipeable_string &s0, const epee::wipeable_string &s1) const
|
||||||
{
|
{
|
||||||
const epee::wipeable_string s0c = utf8canonical(s0);
|
const epee::wipeable_string s0c = tools::utf8canonical(s0, [](wint_t c) -> wint_t { return std::towlower(c); });
|
||||||
const epee::wipeable_string s1c = utf8canonical(s1);
|
const epee::wipeable_string s1c = tools::utf8canonical(s1, [](wint_t c) -> wint_t { return std::towlower(c); });
|
||||||
return s0c == s1c;
|
return s0c == s1c;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -255,6 +255,7 @@ namespace
|
||||||
const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]");
|
const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]");
|
||||||
const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config");
|
const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config");
|
||||||
const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]");
|
const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]");
|
||||||
|
const char* USAGE_MMS_CONFIG_CHECKSUM("mms config_checksum");
|
||||||
const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config");
|
const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config");
|
||||||
const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>");
|
const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>");
|
||||||
const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>");
|
const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>");
|
||||||
|
@ -3465,7 +3466,7 @@ simple_wallet::simple_wallet()
|
||||||
tr("Interface with the MMS (Multisig Messaging System)\n"
|
tr("Interface with the MMS (Multisig Messaging System)\n"
|
||||||
"<subcommand> is one of:\n"
|
"<subcommand> is one of:\n"
|
||||||
" init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n"
|
" init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n"
|
||||||
" send_signer_config, start_auto_config, stop_auto_config, auto_config\n"
|
" send_signer_config, start_auto_config, stop_auto_config, auto_config, config_checksum\n"
|
||||||
"Get help about a subcommand with: help_advanced mms <subcommand>"));
|
"Get help about a subcommand with: help_advanced mms <subcommand>"));
|
||||||
m_cmd_binder.set_handler("mms init",
|
m_cmd_binder.set_handler("mms init",
|
||||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
||||||
|
@ -3534,6 +3535,10 @@ simple_wallet::simple_wallet()
|
||||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
||||||
tr(USAGE_MMS_START_AUTO_CONFIG),
|
tr(USAGE_MMS_START_AUTO_CONFIG),
|
||||||
tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels"));
|
tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels"));
|
||||||
|
m_cmd_binder.set_handler("mms config_checksum",
|
||||||
|
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
||||||
|
tr(USAGE_MMS_CONFIG_CHECKSUM),
|
||||||
|
tr("Get a checksum that allows signers to easily check for identical MMS configuration"));
|
||||||
m_cmd_binder.set_handler("mms stop_auto_config",
|
m_cmd_binder.set_handler("mms stop_auto_config",
|
||||||
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
|
||||||
tr(USAGE_MMS_STOP_AUTO_CONFIG),
|
tr(USAGE_MMS_STOP_AUTO_CONFIG),
|
||||||
|
@ -10366,6 +10371,14 @@ bool simple_wallet::user_confirms(const std::string &question)
|
||||||
return !std::cin.eof() && command_line::is_yes(answer);
|
return !std::cin.eof() && command_line::is_yes(answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool simple_wallet::user_confirms_auto_config()
|
||||||
|
{
|
||||||
|
message_writer(console_color_red, true) << tr("WARNING: Using MMS auto-config mechanisms is not trustless");
|
||||||
|
message_writer() << tr("A malicious auto-config manager could send you info about own wallets instead of other signers' info");
|
||||||
|
message_writer() << tr("If in doubt do not use auto-config or at least compare configs using the \"mms config_checksum\" command");
|
||||||
|
return user_confirms("Accept the risks and continue?");
|
||||||
|
}
|
||||||
|
|
||||||
bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound)
|
bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound)
|
||||||
{
|
{
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
@ -10518,7 +10531,7 @@ void simple_wallet::show_message(const mms::message &m)
|
||||||
case mms::message_type::additional_key_set:
|
case mms::message_type::additional_key_set:
|
||||||
case mms::message_type::note:
|
case mms::message_type::note:
|
||||||
display_content = true;
|
display_content = true;
|
||||||
ms.get_sanitized_message_text(m, sanitized_text);
|
sanitized_text = mms::message_store::get_sanitized_text(m.content, 1000);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
display_content = false;
|
display_content = false;
|
||||||
|
@ -10867,6 +10880,11 @@ void simple_wallet::mms_next(const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!user_confirms_auto_config())
|
||||||
|
{
|
||||||
|
message_writer() << tr("You can use the \"mms delete\" command to delete any unwanted message");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ms.process_signer_config(state, m.content);
|
ms.process_signer_config(state, m.content);
|
||||||
ms.stop_auto_config();
|
ms.stop_auto_config();
|
||||||
|
@ -11193,6 +11211,18 @@ void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args)
|
||||||
list_signers(ms.get_all_signers());
|
list_signers(ms.get_all_signers());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void simple_wallet::mms_config_checksum(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if (args.size() != 0)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << tr("Usage: mms config_checksum");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mms::message_store& ms = m_wallet->get_message_store();
|
||||||
|
LOCK_IDLE_SCOPE();
|
||||||
|
message_writer() << ms.get_config_checksum();
|
||||||
|
}
|
||||||
|
|
||||||
void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args)
|
void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
if (args.size() != 0)
|
if (args.size() != 0)
|
||||||
|
@ -11223,6 +11253,10 @@ void simple_wallet::mms_auto_config(const std::vector<std::string> &args)
|
||||||
fail_msg_writer() << tr("Invalid auto-config token");
|
fail_msg_writer() << tr("Invalid auto-config token");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!user_confirms_auto_config())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
mms::authorized_signer me = ms.get_signer(0);
|
mms::authorized_signer me = ms.get_signer(0);
|
||||||
if (me.auto_config_running)
|
if (me.auto_config_running)
|
||||||
{
|
{
|
||||||
|
@ -11335,6 +11369,10 @@ bool simple_wallet::mms(const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
mms_start_auto_config(mms_args);
|
mms_start_auto_config(mms_args);
|
||||||
}
|
}
|
||||||
|
else if (sub_command == "config_checksum")
|
||||||
|
{
|
||||||
|
mms_config_checksum(mms_args);
|
||||||
|
}
|
||||||
else if (sub_command == "stop_auto_config")
|
else if (sub_command == "stop_auto_config")
|
||||||
{
|
{
|
||||||
mms_stop_auto_config(mms_args);
|
mms_stop_auto_config(mms_args);
|
||||||
|
|
|
@ -478,6 +478,7 @@ namespace cryptonote
|
||||||
void ask_send_all_ready_messages();
|
void ask_send_all_ready_messages();
|
||||||
void check_for_messages();
|
void check_for_messages();
|
||||||
bool user_confirms(const std::string &question);
|
bool user_confirms(const std::string &question);
|
||||||
|
bool user_confirms_auto_config();
|
||||||
bool get_message_from_arg(const std::string &arg, mms::message &m);
|
bool get_message_from_arg(const std::string &arg, mms::message &m);
|
||||||
bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound);
|
bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound);
|
||||||
|
|
||||||
|
@ -498,6 +499,7 @@ namespace cryptonote
|
||||||
void mms_help(const std::vector<std::string> &args);
|
void mms_help(const std::vector<std::string> &args);
|
||||||
void mms_send_signer_config(const std::vector<std::string> &args);
|
void mms_send_signer_config(const std::vector<std::string> &args);
|
||||||
void mms_start_auto_config(const std::vector<std::string> &args);
|
void mms_start_auto_config(const std::vector<std::string> &args);
|
||||||
|
void mms_config_checksum(const std::vector<std::string> &args);
|
||||||
void mms_stop_auto_config(const std::vector<std::string> &args);
|
void mms_stop_auto_config(const std::vector<std::string> &args);
|
||||||
void mms_auto_config(const std::vector<std::string> &args);
|
void mms_auto_config(const std::vector<std::string> &args);
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "serialization/binary_utils.h"
|
#include "serialization/binary_utils.h"
|
||||||
#include "common/base58.h"
|
#include "common/base58.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
#include "common/utf8.h"
|
||||||
#include "string_tools.h"
|
#include "string_tools.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,18 +130,18 @@ void message_store::set_signer(const multisig_wallet_state &state,
|
||||||
authorized_signer &m = m_signers[index];
|
authorized_signer &m = m_signers[index];
|
||||||
if (label)
|
if (label)
|
||||||
{
|
{
|
||||||
m.label = label.get();
|
m.label = get_sanitized_text(label.get(), 50);
|
||||||
}
|
}
|
||||||
if (transport_address)
|
if (transport_address)
|
||||||
{
|
{
|
||||||
m.transport_address = transport_address.get();
|
m.transport_address = get_sanitized_text(transport_address.get(), 200);
|
||||||
}
|
}
|
||||||
if (monero_address)
|
if (monero_address)
|
||||||
{
|
{
|
||||||
m.monero_address_known = true;
|
m.monero_address_known = true;
|
||||||
m.monero_address = monero_address.get();
|
m.monero_address = monero_address.get();
|
||||||
}
|
}
|
||||||
// Save to minimize the chance to loose that info (at least while in beta)
|
// Save to minimize the chance to loose that info
|
||||||
save(state);
|
save(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,6 +203,13 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con
|
||||||
}
|
}
|
||||||
uint32_t num_signers = (uint32_t)signers.size();
|
uint32_t num_signers = (uint32_t)signers.size();
|
||||||
THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers));
|
THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers));
|
||||||
|
for (uint32_t i = 0; i < num_signers; ++i)
|
||||||
|
{
|
||||||
|
authorized_signer &m = signers[i];
|
||||||
|
m.label = get_sanitized_text(m.label, 50);
|
||||||
|
m.transport_address = get_sanitized_text(m.transport_address, 200);
|
||||||
|
m.auto_config_token = get_sanitized_text(m.auto_config_token, 20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
|
void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config)
|
||||||
|
@ -242,10 +250,10 @@ void message_store::process_signer_config(const multisig_wallet_state &state, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
authorized_signer &modify = m_signers[take_index];
|
authorized_signer &modify = m_signers[take_index];
|
||||||
modify.label = m.label; // ALWAYS set label, see comments above
|
modify.label = get_sanitized_text(m.label, 50); // ALWAYS set label, see comments above
|
||||||
if (!modify.me)
|
if (!modify.me)
|
||||||
{
|
{
|
||||||
modify.transport_address = m.transport_address;
|
modify.transport_address = get_sanitized_text(m.transport_address, 200);
|
||||||
modify.monero_address_known = m.monero_address_known;
|
modify.monero_address_known = m.monero_address_known;
|
||||||
if (m.monero_address_known)
|
if (m.monero_address_known)
|
||||||
{
|
{
|
||||||
|
@ -392,6 +400,45 @@ void message_store::process_auto_config_data_message(uint32_t id)
|
||||||
signer.auto_config_running = false;
|
signer.auto_config_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_hash(crypto::hash &sum, const crypto::hash &summand)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < crypto::HASH_SIZE; ++i)
|
||||||
|
{
|
||||||
|
uint32_t x = (uint32_t)sum.data[i];
|
||||||
|
uint32_t y = (uint32_t)summand.data[i];
|
||||||
|
sum.data[i] = (char)((x + y) % 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate a checksum that allows signers to make sure they work with an identical signer config
|
||||||
|
// by exchanging and comparing checksums out-of-band i.e. not using the MMS;
|
||||||
|
// Because different signers have a different order of signers in the config work with "adding"
|
||||||
|
// individual hashes because that operation is commutative
|
||||||
|
std::string message_store::get_config_checksum() const
|
||||||
|
{
|
||||||
|
crypto::hash sum = crypto::null_hash;
|
||||||
|
uint32_t num = SWAP32LE(m_num_authorized_signers);
|
||||||
|
add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num)));
|
||||||
|
num = SWAP32LE(m_num_required_signers);
|
||||||
|
add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num)));
|
||||||
|
for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
|
||||||
|
{
|
||||||
|
const authorized_signer &m = m_signers[i];
|
||||||
|
add_hash(sum, crypto::cn_fast_hash(m.transport_address.data(), m.transport_address.size()));
|
||||||
|
if (m.monero_address_known)
|
||||||
|
{
|
||||||
|
add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_spend_public_key, sizeof(m.monero_address.m_spend_public_key)));
|
||||||
|
add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_view_public_key, sizeof(m.monero_address.m_view_public_key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string checksum_bytes;
|
||||||
|
checksum_bytes += sum.data[0];
|
||||||
|
checksum_bytes += sum.data[1];
|
||||||
|
checksum_bytes += sum.data[2];
|
||||||
|
checksum_bytes += sum.data[3];
|
||||||
|
return epee::string_tools::buff_to_hex_nodelimer(checksum_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
void message_store::stop_auto_config()
|
void message_store::stop_auto_config()
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
|
for (uint32_t i = 0; i < m_num_authorized_signers; ++i)
|
||||||
|
@ -661,32 +708,38 @@ void message_store::delete_all_messages()
|
||||||
m_messages.clear();
|
m_messages.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a message text, which is "attacker controlled data", reasonably safe to display
|
// Make a text, which is "attacker controlled data", reasonably safe to display
|
||||||
// This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command
|
// This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command
|
||||||
void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const
|
std::string message_store::get_sanitized_text(const std::string &text, size_t max_length)
|
||||||
{
|
{
|
||||||
sanitized_text.clear();
|
|
||||||
|
|
||||||
// Restrict the size to fend of DOS-style attacks with heaps of data
|
// Restrict the size to fend of DOS-style attacks with heaps of data
|
||||||
size_t length = std::min(m.content.length(), (size_t)1000);
|
size_t length = std::min(text.length(), max_length);
|
||||||
|
std::string sanitized_text = text.substr(0, length);
|
||||||
|
|
||||||
for (size_t i = 0; i < length; ++i)
|
try
|
||||||
{
|
{
|
||||||
char c = m.content[i];
|
sanitized_text = tools::utf8canonical(sanitized_text, [](wint_t c)
|
||||||
if ((int)c < 32)
|
|
||||||
{
|
{
|
||||||
// Strip out any controls, especially ESC for getting rid of potentially dangerous
|
if ((c < 0x20) || (c == 0x7f) || (c >= 0x80 && c <= 0x9f))
|
||||||
// ANSI escape sequences that a console window might interpret
|
{
|
||||||
c = ' ';
|
// Strip out any controls, especially ESC for getting rid of potentially dangerous
|
||||||
}
|
// ANSI escape sequences that a console window might interpret
|
||||||
else if ((c == '<') || (c == '>'))
|
c = '?';
|
||||||
{
|
}
|
||||||
// Make XML or HTML impossible that e.g. might contain scripts that Qt might execute
|
else if ((c == '<') || (c == '>'))
|
||||||
// when displayed in the GUI wallet
|
{
|
||||||
c = ' ';
|
// Make XML or HTML impossible that e.g. might contain scripts that Qt might execute
|
||||||
}
|
// when displayed in the GUI wallet
|
||||||
sanitized_text += c;
|
c = '?';
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
sanitized_text = "(Illegal UTF-8 string)";
|
||||||
|
}
|
||||||
|
return sanitized_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename)
|
void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename)
|
||||||
|
|
|
@ -242,6 +242,7 @@ namespace mms
|
||||||
size_t add_auto_config_data_message(const multisig_wallet_state &state,
|
size_t add_auto_config_data_message(const multisig_wallet_state &state,
|
||||||
const std::string &auto_config_token);
|
const std::string &auto_config_token);
|
||||||
void process_auto_config_data_message(uint32_t id);
|
void process_auto_config_data_message(uint32_t id);
|
||||||
|
std::string get_config_checksum() const;
|
||||||
void stop_auto_config();
|
void stop_auto_config();
|
||||||
|
|
||||||
// Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command
|
// Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command
|
||||||
|
@ -275,7 +276,7 @@ namespace mms
|
||||||
void set_message_processed_or_sent(uint32_t id);
|
void set_message_processed_or_sent(uint32_t id);
|
||||||
void delete_message(uint32_t id);
|
void delete_message(uint32_t id);
|
||||||
void delete_all_messages();
|
void delete_all_messages();
|
||||||
void get_sanitized_message_text(const message &m, std::string &sanitized_text) const;
|
static std::string get_sanitized_text(const std::string &text, size_t max_length);
|
||||||
|
|
||||||
void send_message(const multisig_wallet_state &state, uint32_t id);
|
void send_message(const multisig_wallet_state &state, uint32_t id);
|
||||||
bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages);
|
bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages);
|
||||||
|
|
Loading…
Reference in New Issue