From f6e703a938a95c555b388f79966cf955c5d07dc6 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Wed, 15 Apr 2020 20:01:25 +0200 Subject: HTTP Auth (Basic) --- Makefile | 1 + base64.cpp | 23 +++++++++++++++++++++++ base64.h | 7 +++++++ config.cpp | 15 +++++++++++++-- config.h | 1 + response.cpp | 28 ++++++++++++++++++++++++++++ webserver.conf | 1 + 7 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 base64.cpp create mode 100644 base64.h diff --git a/Makefile b/Makefile index 84b05ec..c7834d0 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ LIBS+= \ endif PROGSRC=\ + base64.cpp \ config.cpp \ file.cpp \ http.cpp \ diff --git a/base64.cpp b/base64.cpp new file mode 100644 index 0000000..3847f0a --- /dev/null +++ b/base64.cpp @@ -0,0 +1,23 @@ +#include "base64.h" + +#include +#include +#include +#include + +std::string decode64(const std::string &val) +{ + using namespace boost::archive::iterators; + using It = transform_width, 8, 6>; + return boost::algorithm::trim_right_copy_if(std::string(It(std::begin(val)), It(std::end(val))), [](char c) { + return c == '\0'; + }); +} + +std::string encode64(const std::string &val) +{ + using namespace boost::archive::iterators; + using It = base64_from_binary>; + auto tmp = std::string(It(std::begin(val)), It(std::end(val))); + return tmp.append((3 - val.size() % 3) % 3, '='); +} diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..6a3f2c3 --- /dev/null +++ b/base64.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +std::string decode64(const std::string &val); +std::string encode64(const std::string &val); + diff --git a/config.cpp b/config.cpp index 0d9c117..3750bcf 100644 --- a/config.cpp +++ b/config.cpp @@ -63,8 +63,19 @@ void Config::readConfigfile(std::string filename) auto attrs = x.second.get_child(""); path.requested = attrs.get("requested"); for (const auto& param: x.second) { // get all sub-elements of - if (param.first.size() > 0 && param.first[0] != '<') // exclude meta-elements like - path.params[param.first.data()] = param.second.data(); + if (param.first.size() > 0 && param.first[0] != '<') { // exclude meta-elements like + if (param.first == "auth") { + try { + std::string login{param.second.get(".login")}; + std::string password{param.second.get(".password")}; + path.auth[login] = password; + } catch (const std::exception& ex) { + std::cerr << "Warning: Can't read auth data from config: " << ex.what() << std::endl; + } + } else { + path.params[param.first] = param.second.data(); + } + } } site_struct.paths.push_back(path); } else if (x.first == "certpath"s) { diff --git a/config.h b/config.h index 746b95c..46cd267 100644 --- a/config.h +++ b/config.h @@ -13,6 +13,7 @@ struct Path std::string requested; // the requested path // mandatory entries: "plugin", "target", others are optional std::unordered_map params; // what to serve, e.g. which filesystem path (target), and which plugin + std::unordered_map auth; // optional }; struct Site diff --git a/response.cpp b/response.cpp index 8f66c54..0c619a2 100644 --- a/response.cpp +++ b/response.cpp @@ -1,4 +1,6 @@ #include "response.h" + +#include "base64.h" #include "file.h" #include @@ -183,6 +185,8 @@ response_type HttpStatus(std::string status, std::string message, response_type& { res.result(unsigned(stoul(status))); res.set(http::field::content_type, "text/html"); + if (res.result_int() == 401) + res.set(http::field::www_authenticate, "Basic realm=\"Webbox Login\""); res.body() = "

"s + VersionString + " Error

"s + status + " "s + message + "

"s; res.prepare_payload(); @@ -201,6 +205,30 @@ response_type generate_response(request_type& req, Server& server) try { RequestContext req_ctx{req, server}; // can throw std::out_of_range + auto& auth{req_ctx.GetPath().auth}; + if (auth.size() != 0) { + std::string authorization{req[http::field::authorization]}; + if (authorization.substr(0, 6) != "Basic "s) + return HttpStatus("401", "Bad Authorization Type", res); + + authorization = authorization.substr(6); + authorization = decode64(authorization); + + size_t pos {authorization.find(':')}; + if (pos == authorization.npos) + return HttpStatus("401", "Bad Authorization Encoding", res); + + std::string login{authorization.substr(0, pos)}; + std::string password{authorization.substr(pos + 1)}; + + auto it {auth.find(login)}; + if (it == auth.end()) + return HttpStatus("401", "Bad Authorization", res); + + if (it->second != password) + return HttpStatus("401", "Bad Authorization", res); // should be same message as previous one to prevent login guessing + } + plugin_type plugin{req_ctx.GetPlugin()}; auto GetServerParamFunction {std::function(std::bind(GetServerParam, _1, std::ref(server)))}; diff --git a/webserver.conf b/webserver.conf index 35ff2f6..fb5def6 100644 --- a/webserver.conf +++ b/webserver.conf @@ -30,6 +30,7 @@ /home/ernie/testbox Testbox1 0 +
/home/ernie/code/webserver/fullchain.pem /home/ernie/code/webserver/privkey.pem -- cgit v1.2.3