add hashed password capability to endpoint auth by file

pull/1830/head
Jeff 2 years ago
parent a51576d1ea
commit 5050cd0299

@ -243,7 +243,7 @@ if(WITH_HIVE)
endif() endif()
target_link_libraries(liblokinet PUBLIC cxxopts lokinet-platform lokinet-util lokinet-cryptography sqlite_orm ngtcp2_static) 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) if(BUILD_LIBLOKINET)

@ -381,6 +381,15 @@ namespace llarp
stringify("cannot load auth file ", arg, " as it does not seem to exist")}; stringify("cannot load auth file ", arg, " as it does not seem to exist")};
m_AuthFiles.emplace(std::move(arg)); 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>( conf.defineOption<std::string>(
"network", "network",

@ -115,6 +115,7 @@ namespace llarp
std::unordered_map<huint128_t, service::Address> m_mapAddrs; std::unordered_map<huint128_t, service::Address> m_mapAddrs;
service::AuthType m_AuthType = service::AuthType::eAuthTypeNone; 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_AuthUrl;
std::optional<std::string> m_AuthMethod; std::optional<std::string> m_AuthMethod;
std::unordered_set<service::Address> m_AuthWhitelist; std::unordered_set<service::Address> m_AuthWhitelist;

@ -100,6 +100,10 @@ namespace llarp
virtual bool virtual bool
check_identity_privkey(const SecretKey&) = 0; 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; inline Crypto::~Crypto() = default;

@ -13,6 +13,9 @@
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <crypt.h>
#include <llarp/util/str.hpp>
extern "C" extern "C"
{ {
@ -463,6 +466,21 @@ namespace llarp
auto d = keypair.data(); auto d = keypair.data();
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d); 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 } // namespace sodium
const byte_t* const byte_t*

@ -104,6 +104,9 @@ namespace llarp
bool bool
check_identity_privkey(const SecretKey&) override; check_identity_privkey(const SecretKey&) override;
bool
check_passwd_hash(std::string pwhash, std::string challenge) override;
}; };
} // namespace sodium } // namespace sodium

@ -176,7 +176,7 @@ namespace llarp
if (conf.m_AuthType == service::AuthType::eAuthTypeFile) 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) else if (conf.m_AuthType != service::AuthType::eAuthTypeNone)
{ {

@ -37,6 +37,21 @@ namespace llarp::service
return itr->second; 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 /// turn an auth result code into an int
uint64_t uint64_t
AuthResultCodeAsInt(AuthResultCode code) AuthResultCodeAsInt(AuthResultCode code)
@ -67,6 +82,7 @@ namespace llarp::service
class FileAuthPolicy : public IAuthPolicy, public std::enable_shared_from_this<FileAuthPolicy> class FileAuthPolicy : public IAuthPolicy, public std::enable_shared_from_this<FileAuthPolicy>
{ {
const std::set<fs::path> m_Files; const std::set<fs::path> m_Files;
const AuthFileType m_Type;
AbstractRouter* const m_Router; AbstractRouter* const m_Router;
mutable util::Mutex m_Access; mutable util::Mutex m_Access;
std::unordered_set<ConvoTag> m_Pending; std::unordered_set<ConvoTag> m_Pending;
@ -86,8 +102,8 @@ namespace llarp::service
const auto parts = split_any(line, "#;", true); const auto parts = split_any(line, "#;", true);
if (auto part = parts[0]; not parts.empty() and not parts[0].empty()) if (auto part = parts[0]; not parts.empty() and not parts[0].empty())
{ {
// split off whitespaces // split off whitespaces and check password
if (TrimWhitespace(part) == info.token) if (CheckPasswd(std::string{TrimWhitespace(part)}, info.token))
return AuthResult{AuthResultCode::eAuthAccepted, "accepted by whitelist"}; return AuthResult{AuthResultCode::eAuthAccepted, "accepted by whitelist"};
} }
} }
@ -95,9 +111,24 @@ namespace llarp::service
return AuthResult{AuthResultCode::eAuthRejected, "rejected by whitelist"}; 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: public:
FileAuthPolicy(AbstractRouter* r, std::set<fs::path> files) FileAuthPolicy(AbstractRouter* r, std::set<fs::path> files, AuthFileType filetype)
: m_Files{std::move(files)}, m_Router{r} : m_Files{std::move(files)}, m_Type{filetype}, m_Router{r}
{} {}
void void
@ -145,9 +176,9 @@ namespace llarp::service
}; };
std::shared_ptr<IAuthPolicy> 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 } // namespace llarp::service

@ -72,17 +72,29 @@ namespace llarp::service
eAuthTypeWhitelist, eAuthTypeWhitelist,
/// LMQ server /// LMQ server
eAuthTypeLMQ, eAuthTypeLMQ,
/// plain file /// static file
eAuthTypeFile, eAuthTypeFile,
}; };
/// how to interpret an file for auth
enum class AuthFileType
{
eAuthFilePlain,
eAuthFileHashes,
};
/// get an auth type from a string /// get an auth type from a string
/// throws std::invalid_argument if arg is invalid /// throws std::invalid_argument if arg is invalid
AuthType AuthType
ParseAuthType(std::string arg); 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 /// make an IAuthPolicy that reads out of a static file
std::shared_ptr<IAuthPolicy> std::shared_ptr<IAuthPolicy>
MakeFileAuthPolicy(AbstractRouter*, std::set<fs::path> files); MakeFileAuthPolicy(AbstractRouter*, std::set<fs::path> files, AuthFileType fileType);
} // namespace llarp::service } // namespace llarp::service

@ -47,3 +47,44 @@ TEST_CASE("PQ crypto")
REQUIRE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys))); REQUIRE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys)));
REQUIRE(otherShared == shared); 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…
Cancel
Save