diff options
| author | Roland Reichwein <mail@reichwein.it> | 2020-04-15 20:01:25 +0200 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2020-04-15 20:01:25 +0200 | 
| commit | f6e703a938a95c555b388f79966cf955c5d07dc6 (patch) | |
| tree | f47eae93adf9f72b1472ced2a1a051e90845dafe | |
| parent | 1ce0bb7ad50129fbab6c0a75f18eee6149ca1be3 (diff) | |
HTTP Auth (Basic)
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | base64.cpp | 23 | ||||
| -rw-r--r-- | base64.h | 7 | ||||
| -rw-r--r-- | config.cpp | 15 | ||||
| -rw-r--r-- | config.h | 1 | ||||
| -rw-r--r-- | response.cpp | 28 | ||||
| -rw-r--r-- | webserver.conf | 1 | 
7 files changed, 74 insertions, 2 deletions
@@ -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 <boost/archive/iterators/binary_from_base64.hpp> +#include <boost/archive/iterators/base64_from_binary.hpp> +#include <boost/archive/iterators/transform_width.hpp> +#include <boost/algorithm/string.hpp> + +std::string decode64(const std::string &val) +{ + using namespace boost::archive::iterators; + using It = transform_width<binary_from_base64<std::string::const_iterator>, 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<transform_width<std::string::const_iterator, 6, 8>>; + 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 <string> + +std::string decode64(const std::string &val); +std::string encode64(const std::string &val); + @@ -63,8 +63,19 @@ void Config::readConfigfile(std::string filename)         auto attrs = x.second.get_child("<xmlattr>");         path.requested = attrs.get<std::string>("requested");         for (const auto& param: x.second) { // get all sub-elements of <path> -        if (param.first.size() > 0 && param.first[0] != '<') // exclude meta-elements like <xmlattr> -         path.params[param.first.data()] = param.second.data(); +        if (param.first.size() > 0 && param.first[0] != '<') { // exclude meta-elements like <xmlattr> +         if (param.first == "auth") { +          try { +           std::string login{param.second.get<std::string>("<xmlattr>.login")}; +           std::string password{param.second.get<std::string>("<xmlattr>.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) { @@ -13,6 +13,7 @@ struct Path   std::string requested; // the requested path   // mandatory entries: "plugin", "target", others are optional   std::unordered_map<std::string, std::string> params; // what to serve, e.g. which filesystem path (target), and which plugin + std::unordered_map<std::string, std::string> 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 <boost/algorithm/string/predicate.hpp> @@ -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() = "<html><body><h1>"s + VersionString + " Error</h1><p>"s + status + " "s + message + "</p></body></html>"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::string(const std::string& key)>(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 @@      <target>/home/ernie/testbox</target>      <WEBBOX_NAME>Testbox1</WEBBOX_NAME>      <WEBBOX_READONLY>0</WEBBOX_READONLY> +    <auth login="abc" password="def"/>     </path>     <certpath>/home/ernie/code/webserver/fullchain.pem</certpath>     <keypath>/home/ernie/code/webserver/privkey.pem</keypath>  | 
