1
1
Fork 0
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:
Jeff 2022-04-01 12:52:25 -04:00
parent a51576d1ea
commit 5050cd0299
10 changed files with 129 additions and 10 deletions

View file

@ -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)

View file

@ -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",

View file

@ -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;

View file

@ -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;

View file

@ -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*

View file

@ -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

View file

@ -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)
{

View file

@ -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

View file

@ -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

View file

@ -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"));
}