From 0d9fd527f662c6c86109d53cf48c0addcf39b139 Mon Sep 17 00:00:00 2001 From: Wander Nauta Date: Sat, 30 Mar 2024 22:47:32 +0100 Subject: [PATCH] Use cpp-httplib for Authorization header logic instead of OpenSSL The implementation has different (perhaps: less luxurious) behavior: both username and password must now be set (and must be correct) for `checkAuth` to succeed; on authentication failure, the attempted username is no longer logged. In exchange, the implementation is slightly shorter. --- webservice.cc | 74 ++++++--------------------------------------------- 1 file changed, 8 insertions(+), 66 deletions(-) diff --git a/webservice.cc b/webservice.cc index 9d75958..9ac4e9a 100644 --- a/webservice.cc +++ b/webservice.cc @@ -2,8 +2,6 @@ #include #include "httplib.h" #include -#include -#include static std::mutex s_lock; static nlohmann::json s_state; @@ -85,79 +83,23 @@ static void webserverThread(std::unique_ptr svr, string addr) } } - -static int B64Decode(const std::string& src, std::string& dst) -{ - if (src.empty() ) { - dst.clear(); - return 0; - } - int dlen = ( src.length() * 6 + 7 ) / 8 ; - ssize_t olen = 0; - dst.resize(dlen); - BIO *bio, *b64; - bio = BIO_new(BIO_s_mem()); - BIO_write(bio, src.c_str(), src.length()); - b64 = BIO_new(BIO_f_base64()); - bio = BIO_push(b64, bio); - BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); - olen = BIO_read(b64, &dst.at(0), dlen); - if ((olen == 0 || olen == -1) && BIO_should_retry(bio)) { - BIO_free_all(bio); - throw std::runtime_error("BIO_read failed to read all data from memory buffer"); - } - BIO_free_all(bio); - if (olen > 0) { - dst.resize(olen); - return 0; - } - return -1; -} - static std::optional g_webpassword; static std::optional g_webuser; static bool checkAuth(const httplib::Request& req, httplib::Response &res) { - string user; - string password; - string dec; - string::size_type pos; + if (g_webuser && !g_webuser->empty() && g_webpassword && !g_webpassword->empty()) { + const auto& [expectHeader, expectValue] = httplib::make_basic_authentication_header(*g_webuser, *g_webpassword); - - // Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - string auth = req.get_header_value("Authorization"); - if(auth.find("Basic ") != 0) - goto fail; - - if(!g_webpassword || g_webpassword->empty()) - goto fail; + const std::string actualValue = req.get_header_value(expectHeader); - - if(B64Decode(auth.substr(6), dec)) - goto fail; - - pos = dec.find(':'); - if(pos == string::npos) - goto fail; - user = dec.substr(0, pos); - password = dec.substr(pos+1); - - if(g_webuser && !g_webuser->empty() && *g_webuser != user) { - fmt::print("User specified '{}' did not match configured user '{}'\n", - user, *g_webuser); - goto fail; + if (actualValue == expectValue) { + return true; + } else if (!actualValue.empty()) { + fmt::println("Unknown username or wrong password"); + } } - if(g_webpassword != password) { - fmt::print("Wrong password"); - goto fail; - } - - // fmt::print("User '{}', password '{}'\n", user, password); - return true; - - fail:; res.set_header("WWW-Authenticate", "Basic realm=\"Simplomon\""); res.status = 401; return false;