summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2015-06-25 02:14:18 +0200
committerJoel Klinghed <the_jk@yahoo.com>2015-06-25 02:14:18 +0200
commit85adf26a787b33d69d07df0dc786516fed800e21 (patch)
tree12de4b0cae3999f3da7bbf3d21c631eecf6bf4a3
parent0c55606145b6c5d9a303b19b3dba4996ec3ed3a9 (diff)
Add basic auth support
-rw-r--r--src/Makefile.am6
-rw-r--r--src/auth.cc46
-rw-r--r--src/auth.hh25
-rw-r--r--src/base64.cc69
-rw-r--r--src/base64.hh21
-rw-r--r--src/cgi.cc15
-rw-r--r--src/cgi.hh3
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 */
diff --git a/src/cgi.cc b/src/cgi.cc
index b2467f3..ff21541 100644
--- a/src/cgi.cc
+++ b/src/cgi.cc
@@ -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 "";
diff --git a/src/cgi.hh b/src/cgi.hh
index 1403719..37f2c98 100644
--- a/src/cgi.hh
+++ b/src/cgi.hh
@@ -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);