mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
add hashed password capability to endpoint auth by file
This commit is contained in:
parent
a51576d1ea
commit
5050cd0299
10 changed files with 129 additions and 10 deletions
|
@ -243,7 +243,7 @@ if(WITH_HIVE)
|
|||
endif()
|
||||
|
||||
target_link_libraries(liblokinet PUBLIC cxxopts lokinet-platform lokinet-util lokinet-cryptography sqlite_orm ngtcp2_static)
|
||||
target_link_libraries(liblokinet PRIVATE libunbound)
|
||||
target_link_libraries(liblokinet PRIVATE libunbound crypt)
|
||||
|
||||
|
||||
if(BUILD_LIBLOKINET)
|
||||
|
|
|
@ -381,6 +381,15 @@ namespace llarp
|
|||
stringify("cannot load auth file ", arg, " as it does not seem to exist")};
|
||||
m_AuthFiles.emplace(std::move(arg));
|
||||
});
|
||||
conf.defineOption<std::string>(
|
||||
"network",
|
||||
"auth-file-type",
|
||||
ClientOnly,
|
||||
Comment{
|
||||
"How to interpret the contents of an auth file.",
|
||||
"Possible values: hashes, plaintext",
|
||||
},
|
||||
[this](std::string arg) { m_AuthFileType = service::ParseAuthFileType(std::move(arg)); });
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"network",
|
||||
|
|
|
@ -115,6 +115,7 @@ namespace llarp
|
|||
std::unordered_map<huint128_t, service::Address> m_mapAddrs;
|
||||
|
||||
service::AuthType m_AuthType = service::AuthType::eAuthTypeNone;
|
||||
service::AuthFileType m_AuthFileType = service::AuthFileType::eAuthFileHashes;
|
||||
std::optional<std::string> m_AuthUrl;
|
||||
std::optional<std::string> m_AuthMethod;
|
||||
std::unordered_set<service::Address> m_AuthWhitelist;
|
||||
|
|
|
@ -100,6 +100,10 @@ namespace llarp
|
|||
|
||||
virtual bool
|
||||
check_identity_privkey(const SecretKey&) = 0;
|
||||
|
||||
/// check if a password hash string matches the challenge
|
||||
virtual bool
|
||||
check_passwd_hash(std::string pwhash, std::string challenge) = 0;
|
||||
};
|
||||
|
||||
inline Crypto::~Crypto() = default;
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
#include <llarp/util/str.hpp>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <crypt.h>
|
||||
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -463,6 +466,21 @@ namespace llarp
|
|||
auto d = keypair.data();
|
||||
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d);
|
||||
}
|
||||
|
||||
bool
|
||||
CryptoLibSodium::check_passwd_hash(std::string pwhash, std::string challenge)
|
||||
{
|
||||
bool ret = false;
|
||||
auto pos = pwhash.find_last_of('$');
|
||||
auto settings = pwhash.substr(0, pos);
|
||||
crypt_data data{};
|
||||
if (char* ptr = crypt_r(challenge.c_str(), settings.c_str(), &data))
|
||||
{
|
||||
ret = ptr == pwhash;
|
||||
}
|
||||
sodium_memzero(&data, sizeof(data));
|
||||
return ret;
|
||||
}
|
||||
} // namespace sodium
|
||||
|
||||
const byte_t*
|
||||
|
|
|
@ -104,6 +104,9 @@ namespace llarp
|
|||
|
||||
bool
|
||||
check_identity_privkey(const SecretKey&) override;
|
||||
|
||||
bool
|
||||
check_passwd_hash(std::string pwhash, std::string challenge) override;
|
||||
};
|
||||
} // namespace sodium
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace llarp
|
|||
|
||||
if (conf.m_AuthType == service::AuthType::eAuthTypeFile)
|
||||
{
|
||||
m_AuthPolicy = service::MakeFileAuthPolicy(m_router, conf.m_AuthFiles);
|
||||
m_AuthPolicy = service::MakeFileAuthPolicy(m_router, conf.m_AuthFiles, conf.m_AuthFileType);
|
||||
}
|
||||
else if (conf.m_AuthType != service::AuthType::eAuthTypeNone)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,21 @@ namespace llarp::service
|
|||
return itr->second;
|
||||
}
|
||||
|
||||
AuthFileType
|
||||
ParseAuthFileType(std::string data)
|
||||
{
|
||||
std::unordered_map<std::string, AuthFileType> values = {
|
||||
{"plain", AuthFileType::eAuthFilePlain},
|
||||
{"plaintext", AuthFileType::eAuthFilePlain},
|
||||
{"hashed", AuthFileType::eAuthFileHashes},
|
||||
{"hashes", AuthFileType::eAuthFileHashes},
|
||||
{"hash", AuthFileType::eAuthFileHashes}};
|
||||
const auto itr = values.find(data);
|
||||
if (itr == values.end())
|
||||
throw std::invalid_argument("no such auth file type: " + data);
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
/// turn an auth result code into an int
|
||||
uint64_t
|
||||
AuthResultCodeAsInt(AuthResultCode code)
|
||||
|
@ -67,6 +82,7 @@ namespace llarp::service
|
|||
class FileAuthPolicy : public IAuthPolicy, public std::enable_shared_from_this<FileAuthPolicy>
|
||||
{
|
||||
const std::set<fs::path> m_Files;
|
||||
const AuthFileType m_Type;
|
||||
AbstractRouter* const m_Router;
|
||||
mutable util::Mutex m_Access;
|
||||
std::unordered_set<ConvoTag> m_Pending;
|
||||
|
@ -86,8 +102,8 @@ namespace llarp::service
|
|||
const auto parts = split_any(line, "#;", true);
|
||||
if (auto part = parts[0]; not parts.empty() and not parts[0].empty())
|
||||
{
|
||||
// split off whitespaces
|
||||
if (TrimWhitespace(part) == info.token)
|
||||
// split off whitespaces and check password
|
||||
if (CheckPasswd(std::string{TrimWhitespace(part)}, info.token))
|
||||
return AuthResult{AuthResultCode::eAuthAccepted, "accepted by whitelist"};
|
||||
}
|
||||
}
|
||||
|
@ -95,9 +111,24 @@ namespace llarp::service
|
|||
return AuthResult{AuthResultCode::eAuthRejected, "rejected by whitelist"};
|
||||
}
|
||||
|
||||
bool
|
||||
CheckPasswd(std::string hash, std::string challenge) const
|
||||
{
|
||||
switch (m_Type)
|
||||
{
|
||||
case AuthFileType::eAuthFilePlain:
|
||||
return hash == challenge;
|
||||
case AuthFileType::eAuthFileHashes:
|
||||
return CryptoManager::instance()->check_passwd_hash(
|
||||
std::move(hash), std::move(challenge));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
FileAuthPolicy(AbstractRouter* r, std::set<fs::path> files)
|
||||
: m_Files{std::move(files)}, m_Router{r}
|
||||
FileAuthPolicy(AbstractRouter* r, std::set<fs::path> files, AuthFileType filetype)
|
||||
: m_Files{std::move(files)}, m_Type{filetype}, m_Router{r}
|
||||
{}
|
||||
|
||||
void
|
||||
|
@ -145,9 +176,9 @@ namespace llarp::service
|
|||
};
|
||||
|
||||
std::shared_ptr<IAuthPolicy>
|
||||
MakeFileAuthPolicy(AbstractRouter* r, std::set<fs::path> files)
|
||||
MakeFileAuthPolicy(AbstractRouter* r, std::set<fs::path> files, AuthFileType filetype)
|
||||
{
|
||||
return std::make_shared<FileAuthPolicy>(r, std::move(files));
|
||||
return std::make_shared<FileAuthPolicy>(r, std::move(files), filetype);
|
||||
}
|
||||
|
||||
} // namespace llarp::service
|
||||
|
|
|
@ -72,17 +72,29 @@ namespace llarp::service
|
|||
eAuthTypeWhitelist,
|
||||
/// LMQ server
|
||||
eAuthTypeLMQ,
|
||||
/// plain file
|
||||
/// static file
|
||||
eAuthTypeFile,
|
||||
};
|
||||
|
||||
/// how to interpret an file for auth
|
||||
enum class AuthFileType
|
||||
{
|
||||
eAuthFilePlain,
|
||||
eAuthFileHashes,
|
||||
};
|
||||
|
||||
/// get an auth type from a string
|
||||
/// throws std::invalid_argument if arg is invalid
|
||||
AuthType
|
||||
ParseAuthType(std::string arg);
|
||||
|
||||
/// get an auth file type from a string
|
||||
/// throws std::invalid_argument if arg is invalid
|
||||
AuthFileType
|
||||
ParseAuthFileType(std::string arg);
|
||||
|
||||
/// make an IAuthPolicy that reads out of a static file
|
||||
std::shared_ptr<IAuthPolicy>
|
||||
MakeFileAuthPolicy(AbstractRouter*, std::set<fs::path> files);
|
||||
MakeFileAuthPolicy(AbstractRouter*, std::set<fs::path> files, AuthFileType fileType);
|
||||
|
||||
} // namespace llarp::service
|
||||
|
|
|
@ -47,3 +47,44 @@ TEST_CASE("PQ crypto")
|
|||
REQUIRE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys)));
|
||||
REQUIRE(otherShared == shared);
|
||||
}
|
||||
|
||||
TEST_CASE("passwd hash valid")
|
||||
{
|
||||
llarp::sodium::CryptoLibSodium crypto;
|
||||
|
||||
// poggers password hashes
|
||||
std::set<std::string> valid_hashes;
|
||||
// UNIX DES
|
||||
valid_hashes.emplace("CVu85Ms694POo");
|
||||
// sha256 salted
|
||||
valid_hashes.emplace(
|
||||
"$5$cIghotiBGjfPC7Fu$"
|
||||
"TXXxPhpUcEiF9tMnjhEVJFi9AlNDSkNRQFTrXPQTKS9");
|
||||
// sha512 salted
|
||||
valid_hashes.emplace(
|
||||
"$6$qB77ms3wCIo.xVKP$Hl0RLuDgWNmIW4s."
|
||||
"5KUbFmnauoTfrWSPJzDCD8ZTSSfwRbMgqgG6F9y3K.YEYVij8g/"
|
||||
"Js0DRT2RhgXoX0sHGb.");
|
||||
|
||||
for (const auto& hash : valid_hashes)
|
||||
{
|
||||
// make sure it is poggers ...
|
||||
REQUIRE(crypto.check_passwd_hash(hash, "poggers"));
|
||||
// ... and not inscrutible
|
||||
REQUIRE(not crypto.check_passwd_hash(hash, "inscrutible"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("passwd hash malformed")
|
||||
{
|
||||
llarp::sodium::CryptoLibSodium crypto;
|
||||
|
||||
std::set<std::string> invalid_hashes = {
|
||||
"stevejobs",
|
||||
"$JKEDbzgzym1N6", // crypt() for "stevejobs" with a $ at the begining
|
||||
"$0$zero$AAAAAAAAAAA",
|
||||
"$$$AAAAAAAAAAAA",
|
||||
"$LIGMA$BALLS$LMAOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO."};
|
||||
for (const auto& hash : invalid_hashes)
|
||||
REQUIRE(not crypto.check_passwd_hash(hash, "stevejobs"));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue