diff --git a/contrib/frontends/cpp/nntpchan-daemon/nntpchan.cpp b/contrib/frontends/cpp/nntpchan-daemon/nntpchan.cpp index ad575f9..976d361 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/nntpchan.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/nntpchan.cpp @@ -53,6 +53,10 @@ int main(int argc, char * argv[]) { return 1; } + if (nntpconf.find("authdb") != nntpconf.end()) { + nntp.SetLoginDB(nntpconf["authdb"]); + } + auto & a = nntpconf["bind"]; try { diff --git a/contrib/frontends/cpp/nntpchan-daemon/nntpchan.ini b/contrib/frontends/cpp/nntpchan-daemon/nntpchan.ini index 3b50894..cbb82d6 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/nntpchan.ini +++ b/contrib/frontends/cpp/nntpchan-daemon/nntpchan.ini @@ -1,6 +1,6 @@ [nntp] bind = [::]:1199 - +authdb=auth.txt [storage] -path = ./storage/ \ No newline at end of file +path = ./storage/ diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/base64.cpp b/contrib/frontends/cpp/nntpchan-daemon/src/base64.cpp index f36a36c..0150b30 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/base64.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/base64.cpp @@ -219,8 +219,8 @@ namespace nntpchan std::string B64Encode(const uint8_t * data, const std::size_t l) { std::string out; - out.reserve(i2p::data::Base64EncodingBufferSize(l)); - i2p::data::ByteStreamToBase64(data, l, &out[0], out.capacity()); + out.resize(i2p::data::Base64EncodingBufferSize(l)); + i2p::data::ByteStreamToBase64(data, l, &out[0], out.size()); return out; } diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/line.cpp b/contrib/frontends/cpp/nntpchan-daemon/src/line.cpp index 5eebbf5..3682ccc 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/line.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/line.cpp @@ -12,6 +12,9 @@ namespace nntpchan { if(c == '\n') { OnLine(d, idx-2); d += idx; + } else if (c == '\r' && d[idx] == '\n') { + OnLine(d, idx-2); + d += idx + 1; } } } diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.cpp b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.cpp index dfedab8..67f10a7 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.cpp @@ -7,21 +7,18 @@ namespace nntpchan { - HashedCredDB::HashedCredDB(std::istream & i) : - m_instream(i) {} - bool HashedCredDB::CheckLogin(const std::string & user, const std::string & passwd) { std::unique_lock lock(m_access); m_found = false; m_user = user; m_passwd = passwd; - m_instream.seekg(0, std::ios::end); - const auto l = m_instream.tellg(); - m_instream.seekg(0, std::ios::beg); + m_instream->seekg(0, std::ios::end); + const auto l = m_instream->tellg(); + m_instream->seekg(0, std::ios::beg); char * buff = new char[l]; // read file - m_instream.read(buff, l); + m_instream->read(buff, l); OnData(buff, l); delete [] buff; return m_found; @@ -50,6 +47,18 @@ namespace nntpchan return Hash(m_passwd, salt) == cred; } + void HashedCredDB::HandleLine(const std::string &line) + { + if(m_found) return; + if(ProcessLine(line)) + m_found = true; + } + + void HashedCredDB::SetStream(std::istream * s) + { + m_instream = s; + } + std::string HashedCredDB::Hash(const std::string & data, const std::string & salt) { SHA512Digest h; @@ -57,5 +66,33 @@ namespace nntpchan SHA512((const uint8_t*)d.c_str(), d.size(), h); return B64Encode(h.data(), h.size()); } + + HashedFileDB::HashedFileDB(const std::string & fname) : + m_fname(fname), + f(nullptr) + { + + } + + HashedFileDB::~HashedFileDB() + { + } + + void HashedFileDB::Close() + { + if(f.is_open()) + f.close(); + } + + bool HashedFileDB::Open() + { + if(!f.is_open()) + f.open(m_fname); + if(f.is_open()) { + SetStream(&f); + return true; + } + return false; + } } diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.hpp b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.hpp index 8eccf6b..dae6eb2 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.hpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_auth.hpp @@ -2,6 +2,7 @@ #define NNTPCHAN_NNTP_AUTH_HPP #include #include +#include #include #include "line.hpp" @@ -11,27 +12,45 @@ namespace nntpchan class NNTPCredentialDB { public: + /** @brief open connection to database, return false on error otherwise return true */ + virtual bool Open() = 0; + /** @brief close connection to database */ + virtual void Close() = 0; /** @brief return true if username password combo is correct */ virtual bool CheckLogin(const std::string & user, const std::string & passwd) = 0; + virtual ~NNTPCredentialDB() {} }; /** @brief nntp credential db using hashed+salted passwords */ class HashedCredDB : public NNTPCredentialDB, public LineReader { public: - HashedCredDB(std::istream & i); bool CheckLogin(const std::string & user, const std::string & passwd); protected: + void SetStream(std::istream * i); + std::string Hash(const std::string & data, const std::string & salt); + void HandleLine(const std::string & line); private: - bool ProcessLine(const std::string & line); std::mutex m_access; std::string m_user, m_passwd; bool m_found; /** return true if we have a line that matches this username / password combo */ - std::istream & m_instream; + std::istream * m_instream; + }; + + class HashedFileDB : public HashedCredDB + { + public: + HashedFileDB(const std::string & fname); + ~HashedFileDB(); + bool Open(); + void Close(); + private: + std::string m_fname; + std::ifstream f; }; } diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.cpp b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.cpp index 269b692..7b13d7c 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.cpp @@ -8,11 +8,17 @@ namespace nntpchan { NNTPServerHandler::NNTPServerHandler(const std::string & storage) : + m_auth(nullptr), m_store(storage), m_authed(false), m_state(eStateReadCommand) { } + + NNTPServerHandler::~NNTPServerHandler() + { + if(m_auth) delete m_auth; + } void NNTPServerHandler::HandleLine(const std::string &line) { @@ -58,6 +64,7 @@ namespace nntpchan void NNTPServerHandler::SwitchMode(const std::string & mode) { + } void NNTPServerHandler::Quit() @@ -71,4 +78,23 @@ namespace nntpchan { return m_state == eStateQuit; } + + bool NNTPServerHandler::PostingAllowed() + { + return m_authed || m_auth == nullptr; + } + + void NNTPServerHandler::Greet() + { + if(PostingAllowed()) + QueueLine("200 Posting allowed"); + else + QueueLine("201 Posting not allowed"); + } + + void NNTPServerHandler::SetAuth(NNTPCredentialDB *creds) + { + if(m_auth) delete m_auth; + m_auth = creds; + } } diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.hpp b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.hpp index 46554f8..69e277a 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.hpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_handler.hpp @@ -3,6 +3,7 @@ #include #include #include "line.hpp" +#include "nntp_auth.hpp" #include "storage.hpp" namespace nntpchan @@ -11,8 +12,13 @@ namespace nntpchan { public: NNTPServerHandler(const std::string & storage); - + ~NNTPServerHandler(); + bool Done(); + + void SetAuth(NNTPCredentialDB * creds); + + void Greet(); protected: void HandleLine(const std::string & line); @@ -31,9 +37,11 @@ namespace nntpchan // switch nntp modes, this queues a reply void SwitchMode(const std::string & mode); + + bool PostingAllowed(); private: - + NNTPCredentialDB * m_auth; ArticleStorage m_store; std::string m_mode; bool m_authed; diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.cpp b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.cpp index 0867366..351bc6a 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.cpp @@ -1,5 +1,6 @@ #include "buffer.hpp" #include "nntp_server.hpp" +#include "nntp_auth.hpp" #include "net.hpp" #include #include @@ -38,19 +39,32 @@ namespace nntpchan std::cerr << "nntp server OnAccept fail: " << uv_strerror(status) << std::endl; return; } - NNTPServerConn * conn = new NNTPServerConn(m_loop, s, m_storagePath); + NNTPCredentialDB * creds = nullptr; + + std::ifstream i; + i.open(m_logindbpath); + if(i.is_open()) creds = new HashedFileDB(m_logindbpath); + + NNTPServerConn * conn = new NNTPServerConn(m_loop, s, m_storagePath, creds); conn->Greet(); } + void NNTPServer::SetLoginDB(const std::string path) + { + m_logindbpath = path; + } + + void NNTPServer::SetStoragePath(const std::string & path) { m_storagePath = path; } - NNTPServerConn::NNTPServerConn(uv_loop_t * l, uv_stream_t * s, const std::string & storage) : + NNTPServerConn::NNTPServerConn(uv_loop_t * l, uv_stream_t * s, const std::string & storage, NNTPCredentialDB * creds) : m_handler(storage) { + m_handler.SetAuth(creds); uv_tcp_init(l, &m_conn); m_conn.data = this; uv_accept(s, (uv_stream_t*) &m_conn); @@ -81,7 +95,7 @@ namespace nntpchan } }); } - + void NNTPServerConn::SendNextReply() { if(m_handler.HasNextLine()) { @@ -93,7 +107,8 @@ namespace nntpchan void NNTPServerConn::Greet() { - + m_handler.Greet(); + SendNextReply(); } void NNTPServerConn::SendString(const std::string & str) diff --git a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.hpp b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.hpp index 20be6b4..e365ff8 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.hpp +++ b/contrib/frontends/cpp/nntpchan-daemon/src/nntp_server.hpp @@ -23,6 +23,8 @@ namespace nntpchan void OnAccept(uv_stream_t * s, int status); void SetStoragePath(const std::string & path); + + void SetLoginDB(const std::string path); private: @@ -35,6 +37,7 @@ namespace nntpchan std::deque m_conns; + std::string m_logindbpath; std::string m_storagePath; }; @@ -42,8 +45,7 @@ namespace nntpchan class NNTPServerConn { public: - NNTPServerConn(uv_loop_t * l, uv_stream_t * s, const std::string & storage); - + NNTPServerConn(uv_loop_t * l, uv_stream_t * s, const std::string & storage, NNTPCredentialDB * creds); /** @brief close connection, this connection cannot be used after calling this */ void Close(); @@ -63,8 +65,6 @@ namespace nntpchan NNTPServerHandler m_handler; - NNTPCredentialDB * m_logindb; - char m_readbuff[1028]; }; diff --git a/contrib/frontends/cpp/nntpchan-daemon/tool.cpp b/contrib/frontends/cpp/nntpchan-daemon/tool.cpp index 7dfbb67..e2f7095 100644 --- a/contrib/frontends/cpp/nntpchan-daemon/tool.cpp +++ b/contrib/frontends/cpp/nntpchan-daemon/tool.cpp @@ -1,18 +1,91 @@ #include "base64.hpp" #include "crypto.hpp" +#include +#include #include #include +#include static void print_help(const std::string & exename) { - std::cout << "usage: " << exename << " [help|passwd|genconf]" << std::endl; + std::cout << "usage: " << exename << " [help|gencred|checkcred]" << std::endl; +} + +static void print_long_help() { + +} + +static void gen_passwd(const std::string & username, const std::string & passwd) +{ + std::array random; + randombytes_buf(random.data(), random.size()); + std::string salt = nntpchan::B64Encode(random.data(), random.size()); + std::string cred = passwd + salt; + nntpchan::SHA512Digest d; + nntpchan::SHA512((const uint8_t *)cred.c_str(), cred.size(), d); + std::string hash = nntpchan::B64Encode(d.data(), d.size()); + std::cout << username << ":" << hash << ":" << salt << std::endl; +} + + +static bool check_cred(const std::string & cred, const std::string & passwd) +{ + auto idx = cred.find(":"); + if(idx == std::string::npos || idx == 0) return false; + std::string part = cred.substr(idx+1); + idx = part.find(":"); + if(idx == std::string::npos || idx == 0) return false; + std::string salt = part.substr(idx+1); + std::string hash = part.substr(0, idx); + std::vector h; + if(!nntpchan::B64Decode(hash, h)) return false; + nntpchan::SHA512Digest d; + std::string l = passwd + salt; + nntpchan::SHA512((const uint8_t*)l.data(), l.size(), d); + return std::memcmp(h.data(), d.data(), d.size()) == 0; } int main(int argc, char * argv[]) { + assert(sodium_init() == 0); if(argc == 1) { print_help(argv[0]); return 1; } + std::string cmd(argv[1]); + if (cmd == "help") { + print_long_help(); + return 0; + } + if (cmd == "gencred") { + if(argc == 4) { + gen_passwd(argv[2], argv[3]); + return 0; + } else { + std::cout << "usage: " << argv[0] << " passwd username password" << std::endl; + return 1; + } + } + if(cmd == "checkcred" ) { + std::string cred; + std::cout << "credential: " ; + if(!std::getline(std::cin, cred)) { + return 1; + } + std::string passwd; + std::cout << "password: "; + if(!std::getline(std::cin, passwd)) { + return 1; + } + if(check_cred(cred, passwd)) { + std::cout << "okay" << std::endl; + return 0; + } + std::cout << "bad login" << std::endl; + return 1; + } + + print_help(argv[0]); + return 1; }