diff options
| -rw-r--r-- | src/Makefile.am | 6 | ||||
| -rw-r--r-- | src/auth.cc | 46 | ||||
| -rw-r--r-- | src/auth.hh | 25 | ||||
| -rw-r--r-- | src/base64.cc | 69 | ||||
| -rw-r--r-- | src/base64.hh | 21 | ||||
| -rw-r--r-- | src/cgi.cc | 15 | ||||
| -rw-r--r-- | src/cgi.hh | 3 |
7 files changed, 183 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8a6ee24..cfb56e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,7 +29,8 @@ libcgi_la_SOURCES = cgi.hh common.hh cgi.cc \ header_parser.hh header_parser.cc \ args.hh args.cc \ http.hh http.cc \ - multipart_formdata_parser.hh multipart_formdata_parser.cc + auth.hh auth.cc \ + multipart_formdata_parser.hh multipart_formdata_parser.cc libcgi_la_CPPFLAGS = $(AM_CPPFLAGS) @FASTCGI_CFLAGS@ libcgi_la_LIBADD = @FASTCGI_LIBS@ libutil.la @@ -38,7 +39,8 @@ libdb_la_CPPFLAGS = $(AM_CPPFLAGS) @SQLITE3_CFLAGS@ libdb_la_LIBADD = @SQLITE3_LIBS@ libutil_la_SOURCES = common.hh fsutils.cc fsutils.hh config.cc config.hh \ - strutils.hh strutils.cc sockutils.hh sockutils.cc + strutils.hh strutils.cc sockutils.hh sockutils.cc \ + base64.hh base64.cc libsender_client_la_SOURCES = common.h sender_client.cc sender_client.hh libsender_client_la_LIBADD = libutil.la diff --git a/src/auth.cc b/src/auth.cc new file mode 100644 index 0000000..015b2fc --- /dev/null +++ b/src/auth.cc @@ -0,0 +1,46 @@ +#include "common.hh" + +#include "auth.hh" +#include "base64.hh" +#include "cgi.hh" +#include "http.hh" +#include "strutils.hh" + +namespace stuff { + +bool Auth::auth(CGI* cgi, const std::string& realm, const std::string& passwd, + std::string* user) { + auto auth = cgi->http_auth(); + auto pos = auth.find(' '); + if (pos != std::string::npos) { + if (ascii_tolower(auth.substr(0, pos)) == "basic") { + std::string tmp; + if (Base64::decode(auth.substr(pos + 1), &tmp)) { + pos = tmp.find(':'); + if (pos != std::string::npos) { + if (tmp.substr(pos + 1) == passwd) { + if (user) user->assign(tmp.substr(0, pos)); + return true; + } + } + } + } + } + + std::map<std::string, std::string> headers; + std::string tmp = realm; + for (auto it = tmp.begin(); it != tmp.end(); ++it) { + if (!((*it >= 'a' && *it <= 'z') || + (*it >= 'A' && *it <= 'Z') || + (*it >= '0' && *it <= '9') || + *it == '-' || *it == '_' || *it == '.' || *it == ' ')) { + *it = '.'; + } + } + headers.insert(std::make_pair("WWW-Authenticate", + "Basic realm=\"" + tmp + "\"")); + Http::response(401, headers, "Authentication needed"); + return false; +} + +} // namespace stuff diff --git a/src/auth.hh b/src/auth.hh new file mode 100644 index 0000000..ba98b41 --- /dev/null +++ b/src/auth.hh @@ -0,0 +1,25 @@ +#ifndef AUTH_HH +#define AUTH_HH + +#include <string> + +namespace stuff { + +class CGI; + +class Auth { +public: + static bool auth(CGI* cgi, const std::string& realm, + const std::string& passwd, + std::string* user); + +private: + Auth() = delete; + ~Auth() = delete; + Auth(Auth&) = delete; + Auth& operator=(Auth&) = delete; +}; + +} // namespace stuff + +#endif /* AUTH_HH */ diff --git a/src/base64.cc b/src/base64.cc new file mode 100644 index 0000000..da72de1 --- /dev/null +++ b/src/base64.cc @@ -0,0 +1,69 @@ +#include "common.hh" + +#include "base64.hh" + +namespace stuff { + +namespace { + +int8_t value(char c) { + if (c >= 'A' && c <= 'Z') { + return c - 'A'; + } + if (c >= 'a' && c <= 'z') { + return 26 + c - 'a'; + } + if (c >= '0' && c <= '9') { + return 52 + c - '0'; + } + if (c == '+' || c == '-') { + return 62; + } + if (c == '/' || c == '_') { + return 63; + } + return -1; +} + +bool decode_one(const int8_t buf[4], std::string* output) { + if (buf[1] == -1 || buf[0] == -1) { + // There is no reason for the first or the second char to be + // padding + return false; + } + output->push_back((buf[0] << 2) | (buf[1] >> 4)); + if (buf[2] == -1) { + return buf[3] == -1; // No padding between non-padding + } + output->push_back(((buf[1] & 0xf) << 4) | (buf[2] >> 2)); + if (buf[3] == -1) return true; + output->push_back(((buf[2] & 0x3) << 6) | buf[3]); + return true; +} + +} // namespace + +bool Base64::decode(const std::string& input, std::string* output) { + int8_t buf[4]; + size_t fill = 0; + bool done = false; + output->clear(); + for (auto it = input.begin(); it != input.end(); ++it) { + buf[fill] = value(*it); + if (buf[fill] != -1 || *it == '=') { + if (done) return false; // crap after padding + if (++fill == 4) { + if (!decode_one(buf, output)) return false; + done = buf[3] == -1; + fill = 0; + } + } + } + if (fill != 0) { + while (fill < 4) buf[fill] = -1; + if (!decode_one(buf, output)) return false; + } + return true; +} + +} // namespace stuff diff --git a/src/base64.hh b/src/base64.hh new file mode 100644 index 0000000..1790a7a --- /dev/null +++ b/src/base64.hh @@ -0,0 +1,21 @@ +#ifndef BASE64_HH +#define BASE64_HH + +#include <string> + +namespace stuff { + +class Base64 { +public: + static bool decode(const std::string& input, std::string* output); + +private: + Base64() = delete; + ~Base64() = delete; + Base64(Base64&) = delete; + Base64& operator=(Base64&) = delete; +}; + +} // namespace stuff + +#endif /* BASE64_HH */ @@ -63,6 +63,21 @@ public: return path ? path : ""; } + std::string request_uri() override { + auto path = getparam("REQUEST_URI"); + return path ? path : ""; + } + + std::string remote_addr() override { + auto path = getparam("REMOTE_ADDR"); + return path ? path : ""; + } + + std::string http_auth() override { + auto auth = getparam("HTTP_AUTHORIZATION"); + return auth ? auth : ""; + } + std::string content_type() override { auto ct = getparam("CONTENT_TYPE"); if (!ct) return ""; @@ -27,8 +27,11 @@ public: // Will first try post_data and fallback to query_data void get_data(std::map<std::string,std::string>* data); virtual std::string request_path() = 0; + virtual std::string request_uri() = 0; + virtual std::string remote_addr() = 0; virtual request_type request_type() = 0; virtual std::string content_type() = 0; + virtual std::string http_auth() = 0; static int run(std::function<bool(CGI*)> handle_request); |
