RFC 6750 OAuth 2.0 Authorization Framework: Bearer Token Usage
The attached patch adds a minimal implementation of Bearer
authentication scheme to Squid. It consists of three components:
1) Squid build system infrastructure for building Bearer authentication
2) A testing fake-auth helper (bearer_fake_auth).
Helper which takes Bearer helper input an always returns OK.
3) Bearer authentication library ("module") for Squid.
* implements the logics for squid.conf "Bearer" auth_param scheme and
necessary configuration options.
* implements the helper management and API for Bearer helpers.
* implements logics for www-auth and proxy-auth header parsing and
generating.
At present no restriction between HTTP and HTTPS is defined by Squid.
Challenges will be made for both. It is left to the client to ensure
adequate security on the connection it sends Bearer tokens.
* implements helper driven TTL for token caching.
Due to significant security risks with Bearer tokens the TTL is not
configurable from squid.conf. Instead the helper is expected to provide
a ttl= parameter from the auth backend explicitly determining the time
in seconds for which each response may be cached and re-used. In absence
of ttl= value the helper response is treated as already expired (a nonce).
A garbage collection TTL "cleanup_interval" is configurable and removes
cache entries which have been stale for at least 1 hr.
* uses a default token scope of "proxy:HTTP" for generic HTTP proxies
NOTES:
* At present no web browsers implement Bearer authentication in
response to a proxy-authenticate challenge.
- However some of the common browsers should support Bearer
authentication with reverse proxies over HTTPS (Firefox and IE
apparently, not Chrome).
- command line tools and AJAX / XHR implementations which allow header
customisation can be scripted to support Bearer.
* This is only a minimal implementation, emitting only the realm= and
scope= parameters to clients.
- The key_extras mechanism can be used to pass extension client request
parameters to the Bearer helper.
- Extension parameters in Squid responses is not supported.
* Bearer authentication to cache_peers is not supported explicitly.
- implicit support exists with login=PASSTHRU, which may be used to
relay Bearer tokens for SSO to multiple proxies.
Amos
=== modified file 'configure.ac'
--- configure.ac 2014-07-13 08:49:42 +0000
+++ configure.ac 2014-07-30 23:59:58 +0000
@@ -1765,40 +1765,53 @@
SQUID_YESNO([$enableval],
[unrecognized argument to --enable-auth: $enableval])
])
AC_MSG_NOTICE([Authentication support enabled: ${enable_auth:=yes}])
SQUID_DEFINE_BOOL(USE_AUTH,$enable_auth,[Enable support for authentication])
AM_CONDITIONAL(ENABLE_AUTH, test "x$enable_auth" != "xno")
AUTH_MODULES=""
AC_ARG_ENABLE(auth-basic,
AS_HELP_STRING([--enable-auth-basic="list of helpers"],
[Enable the basic authentication scheme, and build the specified helpers.
Not providing an explicit list of helpers will attempt build of
all possible helpers. Default is to do so.
To disable the basic authentication scheme, use --disable-auth-basic.
To enable but build no helpers, specify "none".
To see available helpers, see the helpers/basic_auth directory. ]),[
#nothing to do really
])
m4_include([helpers/basic_auth/modules.m4])
+AC_ARG_ENABLE(auth-bearer,
+ AS_HELP_STRING([--enable-auth-bearer="list of helpers"],
+ [Enable the OAuth 2.0 Bearer authentication scheme, and build the
+ specified helpers.
+ Not providing an explicit list of helpers will attempt build of
+ all possible helpers. Default is to do so.
+ To disable the Bearer authentication scheme, use --disable-auth-bearer.
+ To enable but build no helpers, specify "none".
+ To see available helpers, see the helpers/bearer_auth directory. ]),[
+#nothing to do really
+])
+m4_include([helpers/bearer_auth/modules.m4])
+
AC_ARG_ENABLE(auth-ntlm,
AS_HELP_STRING([--enable-auth-ntlm="list of helpers"],
[Enable the NTLM authentication scheme, and build the specified helpers.
Not providing an explicit list of helpers will attempt build of
all possible helpers. Default is to do so.
To disable the NTLM authentication scheme, use --disable-auth-ntlm.
To enable but build no helpers, specify "none".
To see available helpers, see the helpers/ntlm_auth directory. ]),[
])
m4_include([helpers/ntlm_auth/modules.m4])
AC_ARG_ENABLE(auth-negotiate,
AS_HELP_STRING([--enable-auth-negotiate="list of helpers"],
[Enable the Negotiate authentication scheme, and build the specified
helpers.
Not providing an explicit list of helpers will attempt build of
all possible helpers. Default is to do so.
To disable the Negotiate authentication scheme,
use --disable-auth-negotiate.
To enable but build no helpers, specify "none".
@@ -3446,40 +3459,41 @@
AC_CONFIG_FILES([
Makefile
compat/Makefile
lib/Makefile
lib/ntlmauth/Makefile
lib/libTrie/Makefile
lib/libTrie/test/Makefile
lib/profiler/Makefile
lib/rfcnb/Makefile
lib/smblib/Makefile
lib/snmplib/Makefile
scripts/Makefile
src/Makefile
src/anyp/Makefile
src/base/Makefile
src/acl/Makefile
src/fs/Makefile
src/repl/Makefile
src/auth/Makefile
src/auth/basic/Makefile
+ src/auth/bearer/Makefile
src/auth/digest/Makefile
src/auth/negotiate/Makefile
src/auth/ntlm/Makefile
src/adaptation/Makefile
src/adaptation/icap/Makefile
src/adaptation/ecap/Makefile
src/comm/Makefile
src/esi/Makefile
src/eui/Makefile
src/format/Makefile
src/http/Makefile
src/icmp/Makefile
src/ident/Makefile
src/ip/Makefile
src/log/Makefile
src/ipc/Makefile
src/ssl/Makefile
src/mgr/Makefile
src/parser/Makefile
src/snmp/Makefile
@@ -3488,40 +3502,42 @@
errors/Makefile
test-suite/Makefile
doc/Makefile
doc/manuals/Makefile
helpers/Makefile
helpers/basic_auth/Makefile
helpers/basic_auth/DB/Makefile
helpers/basic_auth/fake/Makefile
helpers/basic_auth/getpwnam/Makefile
helpers/basic_auth/LDAP/Makefile
helpers/basic_auth/MSNT/Makefile
helpers/basic_auth/MSNT-multi-domain/Makefile
helpers/basic_auth/NCSA/Makefile
helpers/basic_auth/NIS/Makefile
helpers/basic_auth/PAM/Makefile
helpers/basic_auth/POP3/Makefile
helpers/basic_auth/RADIUS/Makefile
helpers/basic_auth/SASL/Makefile
helpers/basic_auth/SMB/Makefile
helpers/basic_auth/SSPI/Makefile
+ helpers/bearer_auth/Makefile
+ helpers/bearer_auth/fake/Makefile
helpers/digest_auth/Makefile
helpers/digest_auth/eDirectory/Makefile
helpers/digest_auth/file/Makefile
helpers/digest_auth/LDAP/Makefile
helpers/ntlm_auth/Makefile
helpers/ntlm_auth/fake/Makefile
helpers/ntlm_auth/smb_lm/Makefile
helpers/ntlm_auth/SSPI/Makefile
helpers/negotiate_auth/Makefile
helpers/negotiate_auth/kerberos/Makefile
helpers/negotiate_auth/SSPI/Makefile
helpers/negotiate_auth/wrapper/Makefile
helpers/external_acl/Makefile
helpers/external_acl/AD_group/Makefile
helpers/external_acl/delayer/Makefile
helpers/external_acl/eDirectory_userip/Makefile
helpers/external_acl/file_userip/Makefile
helpers/external_acl/kerberos_ldap_group/Makefile
helpers/external_acl/LDAP_group/Makefile
helpers/external_acl/LM_group/Makefile
=== modified file 'helpers/Makefile.am'
--- helpers/Makefile.am 2013-07-09 11:15:51 +0000
+++ helpers/Makefile.am 2014-05-22 09:19:25 +0000
@@ -1,30 +1,32 @@
EXTRA_DIST = defines.h
DIST_SUBDIRS = \
basic_auth \
+ bearer_auth \
digest_auth \
external_acl \
log_daemon \
negotiate_auth \
ntlm_auth \
url_rewrite \
ssl \
storeid_rewrite
SUBDIRS = \
basic_auth \
+ bearer_auth \
digest_auth \
external_acl \
log_daemon \
negotiate_auth \
url_rewrite \
storeid_rewrite
if ENABLE_AUTH_NTLM
SUBDIRS += ntlm_auth
endif
if ENABLE_SSL
SUBDIRS += ssl
endif
=== added directory 'helpers/bearer_auth'
=== added file 'helpers/bearer_auth/Makefile.am'
--- helpers/bearer_auth/Makefile.am 1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/Makefile.am 2014-05-22 09:19:10 +0000
@@ -0,0 +1,7 @@
+## Alphabetical list of sub-directories to distribute with Squid:
+DIST_SUBDIRS = \
+ fake
+
+SUBDIRS = $(BEARER_AUTH_HELPERS)
+
+EXTRA_DIST = modules.m4
=== added directory 'helpers/bearer_auth/fake'
=== added file 'helpers/bearer_auth/fake/Makefile.am'
--- helpers/bearer_auth/fake/Makefile.am 1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/fake/Makefile.am 2014-05-22 09:19:10 +0000
@@ -0,0 +1,8 @@
+include $(top_srcdir)/src/Common.am
+
+libexec_PROGRAMS = bearer_fake_auth
+bearer_fake_auth_SOURCES = fake.cc
+
+LDADD = $(COMPAT_LIB)
+
+EXTRA_DIST = required.m4
=== added file 'helpers/bearer_auth/fake/fake.cc'
--- helpers/bearer_auth/fake/fake.cc 1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/fake/fake.cc 2014-05-26 07:54:14 +0000
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, Treehouse Networks Ltd. New Zealand
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Fake Bearer Authentication program for Squid.
+ *
+ * This code gets the user details and returns OK.
+ * It is intended for testing use and as a base for further implementation.
+ */
+
+#include "squid.h"
+#include "helpers/defines.h"
+
+#include <cstring>
+
+/**
+ * options:
+ * -d enable debugging.
+ * -h interface help.
+ */
+char *program_name = NULL;
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: %s [-d] [-v] [-h]\n"
+ " -d enable debugging.\n"
+ " -h this message\n\n",
+ program_name);
+}
+
+static void
+process_options(int argc, char *argv[])
+{
+ int opt;
+
+ opterr = 0;
+ while (-1 != (opt = getopt(argc, argv, "hd"))) {
+ switch (opt) {
+ case 'd':
+ debug_enabled = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ fprintf(stderr, "%s: FATAL: unknown option: -%c. Exiting\n",
program_name, opt);
+ usage();
+ exit(1);
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buf[HELPER_INPUT_BUFFER];
+ int buflen = 0;
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ program_name = argv[0];
+
+ process_options(argc, argv);
+
+ debug("%s build " __DATE__ ", " __TIME__ " starting up...\n",
program_name);
+
+ while (fgets(buf, HELPER_INPUT_BUFFER, stdin) != NULL) {
+ char *p;
+
+ if ((p = strchr(buf, '\n')) != NULL) {
+ *p = '\0'; /* strip \n */
+ buflen = p - buf; /* length is known already */
+ } else
+ buflen = strlen(buf); /* keep this so we only scan the buffer
for \0 once per loop */
+
+ debug("Got %d bytes '%s' from Squid\n", buflen, buf);
+
+ /* send 'OK' result back to Squid */
+ fprintf(stdout, "OK user=fake/%s ttl=60\n", buf);
+ }
+ debug("%s build " __DATE__ ", " __TIME__ " shutting down...\n",
program_name);
+ return 0;
+}
=== added file 'helpers/bearer_auth/fake/required.m4'
--- helpers/bearer_auth/fake/required.m4 1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/fake/required.m4 2014-05-21 06:20:59 +0000
@@ -0,0 +1 @@
+BUILD_HELPER="fake"
=== added file 'helpers/bearer_auth/modules.m4'
--- helpers/bearer_auth/modules.m4 1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/modules.m4 2014-05-21 06:24:16 +0000
@@ -0,0 +1,56 @@
+# This file is supposed to run all the tests required to identify which
+# configured modules are able to be built in this environment
+
+# FIXME: de-duplicate $enable_auth_bearer list containing double entries.
+
+#not specified. Inherit global
+if test "x$enable_auth_bearer" = "x"; then
+ enable_auth_bearer=$enable_auth
+fi
+#conflicts with global
+if test "x$enable_auth_bearer" != "xno" -a "x$enable_auth" = "xno" ; then
+ AC_MSG_ERROR([Bearer auth requested but auth disabled])
+fi
+#define list of modules to build
+if test "x$enable_auth_bearer" = "xyes" ; then
+ SQUID_LOOK_FOR_MODULES([$srcdir/helpers/bearer_auth],[enable_auth_bearer])
+fi
+#handle the "none" special case
+if test "x$enable_auth_bearer" = "xnone" ; then
+ enable_auth_bearer=""
+fi
+
+BEARER_AUTH_HELPERS=""
+#enable_auth_bearer contains either "no" or the list of modules to be built
+enable_auth_bearer="`echo $enable_auth_bearer| sed -e 's/,/ /g;s/ */ /g'`"
+if test "x$enable_auth_bearer" != "xno" ; then
+ AUTH_MODULES="$AUTH_MODULES bearer"
+ AC_DEFINE([HAVE_AUTH_MODULE_BEARER],1,[Bearer auth module is built])
+ for helper in $enable_auth_bearer; do
+ dir="$srcdir/helpers/bearer_auth/$helper"
+
+ # modules converted to autoconf macros already
+ # NP: we only need this list because m4_include() does not accept
variables
+ if test "x$helper" = "xfake" ; then
+ m4_include([helpers/bearer_auth/fake/required.m4])
+
+ # modules not yet converted to autoconf macros (or third party drop-in's)
+ elif test -f "$dir/config.test" && sh "$dir/config.test"
"$squid_host_os"; then
+ BUILD_HELPER="$helper"
+ fi
+
+ if test -d "$srcdir/helpers/bearer_auth/$helper"; then
+ if test "$BUILD_HELPER" != "$helper"; then
+ AC_MSG_NOTICE([Bearer auth helper $helper ... found but cannot be
built])
+ else
+ BEARER_AUTH_HELPERS="$BEARER_AUTH_HELPERS $BUILD_HELPER"
+ fi
+ else
+ AC_MSG_ERROR([Bearer auth helper $helper ... not found])
+ fi
+ done
+fi
+
+AC_MSG_NOTICE([Bearer auth helpers to be built: $BEARER_AUTH_HELPERS])
+AM_CONDITIONAL(ENABLE_AUTH_BEARER, test "x$enable_auth_bearer" != "xno")
+AC_SUBST(BEARER_AUTH_HELPERS)
=== modified file 'src/AuthReg.cc'
--- src/AuthReg.cc 2012-08-31 16:57:39 +0000
+++ src/AuthReg.cc 2014-05-22 09:21:50 +0000
@@ -1,48 +1,55 @@
#include "squid.h"
#if USE_AUTH
#include "AuthReg.h"
#if HAVE_AUTH_MODULE_BASIC
#include "auth/basic/Scheme.h"
#endif
+#if HAVE_AUTH_MODULE_BEARER
+#include "auth/bearer/Scheme.h"
+#endif
#if HAVE_AUTH_MODULE_DIGEST
#include "auth/digest/Scheme.h"
#endif
#if HAVE_AUTH_MODULE_NEGOTIATE
#include "auth/negotiate/Scheme.h"
#endif
#if HAVE_AUTH_MODULE_NTLM
#include "auth/ntlm/Scheme.h"
#endif
#include "Debug.h"
/**
* Initialize the authentication modules (if any)
* This is required once, before any configuration actions are taken.
*/
void
Auth::Init()
{
debugs(29,DBG_IMPORTANT,"Startup: Initializing Authentication Schemes
...");
#if HAVE_AUTH_MODULE_BASIC
static const char *basic_type = Auth::Basic::Scheme::GetInstance()->type();
debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" <<
basic_type << "'");
#endif
+#if HAVE_AUTH_MODULE_BEARER
+ static const char *bearer_type =
Auth::Bearer::Scheme::GetInstance()->type();
+ debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" <<
bearer_type << "'");
+#endif
#if HAVE_AUTH_MODULE_DIGEST
static const char *digest_type =
Auth::Digest::Scheme::GetInstance()->type();
debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" <<
digest_type << "'");
#endif
#if HAVE_AUTH_MODULE_NEGOTIATE
static const char *negotiate_type =
Auth::Negotiate::Scheme::GetInstance()->type();
debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" <<
negotiate_type << "'");
#endif
#if HAVE_AUTH_MODULE_NTLM
static const char *ntlm_type = Auth::Ntlm::Scheme::GetInstance()->type();
debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" <<
ntlm_type << "'");
#endif
debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication.");
}
#endif /* USE_AUTH */
=== modified file 'src/auth/Makefile.am'
--- src/auth/Makefile.am 2013-10-31 04:39:36 +0000
+++ src/auth/Makefile.am 2014-05-22 09:21:41 +0000
@@ -1,25 +1,25 @@
include $(top_srcdir)/src/Common.am
include $(top_srcdir)/src/TestHeaders.am
SUBDIRS = $(AUTH_MODULES)
-DIST_SUBDIRS = basic digest negotiate ntlm
+DIST_SUBDIRS = basic bearer digest negotiate ntlm
noinst_LTLIBRARIES = libauth.la libacls.la
## not needed? $(AUTH_LIBS_TO_BUILD)
## EXTRA_LTLIBRARIES = libdigest.la libntlm.la libnegotiate.la
## authentication framework; this library is always built
libauth_la_SOURCES = \
Type.h \
Type.cc \
Config.cc \
Config.h \
CredentialState.cc \
CredentialState.h \
Gadgets.cc \
Gadgets.h \
QueueNode.h \
Scheme.cc \
Scheme.h \
State.h \
State.cc \
=== modified file 'src/auth/QueueNode.h'
--- src/auth/QueueNode.h 2013-03-25 05:41:24 +0000
+++ src/auth/QueueNode.h 2014-05-29 06:14:35 +0000
@@ -1,26 +1,31 @@
#ifndef SQUID_SRC_AUTH_QUEUENODE_H
#define SQUID_SRC_AUTH_QUEUENODE_H
+// NP: keep in sync with auth/UserRequest.h definition
+typedef void AUTHCB(void*);
+
namespace Auth
{
+class UserRequest;
+
/**
* A queue of auth requests waiting for verification to occur.
*
* Certain authentication schemes such a Basic and Bearer auth
* permit credentials tokens to be repeated from multiple sources
* simultaneously. This queue node allows multiple validation
* queries to be collapsed into one backend helper lookup.
* CBDATA and handlers stored in these queue nodes can be notified
* all at once with a result when the lookup completes.
*/
class QueueNode
{
private:
// we store CBDATA here, copy is not safe
QueueNode(const QueueNode &);
QueueNode &operator =(const QueueNode &);
public:
QueueNode(Auth::UserRequest *aRequest, AUTHCB *aHandler, void *aData) :
=== modified file 'src/auth/Type.h'
--- src/auth/Type.h 2011-03-28 13:47:37 +0000
+++ src/auth/Type.h 2014-05-22 09:21:48 +0000
@@ -1,23 +1,24 @@
#ifndef _SQUID__SRC_AUTH_AUTHTYPE_H
#define _SQUID__SRC_AUTH_AUTHTYPE_H
#if USE_AUTH
namespace Auth
{
typedef enum {
AUTH_UNKNOWN, /* default */
AUTH_BASIC,
+ AUTH_BEARER,
AUTH_NTLM,
AUTH_DIGEST,
AUTH_NEGOTIATE,
AUTH_BROKEN /* known type, but broken data */
} Type;
extern const char *Type_str[];
}; // namespace Auth
#endif /* USE_AUTH */
#endif
=== added directory 'src/auth/bearer'
=== added file 'src/auth/bearer/Config.cc'
--- src/auth/bearer/Config.cc 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Config.cc 2014-07-31 00:21:43 +0000
@@ -0,0 +1,253 @@
+/*
+ * DEBUG: section 29 Bearer Authentication
+ */
+#include "squid.h"
+#include "auth/bearer/Config.h"
+#include "auth/bearer/Scheme.h"
+#include "auth/bearer/Token.h"
+#include "auth/bearer/User.h"
+#include "auth/bearer/UserRequest.h"
+#include "base/CharacterSet.h"
+#include "cache_cf.h"
+#include "HttpHeaderTools.h"
+#include "HttpReply.h"
+#include "mgr/Registration.h"
+#include "Store.h"
+#include "wordlist.h"
+
+// Bearer Scheme statistics
+static AUTHSSTATS authenticateBearerStats;
+
+helper *bearerauthenticators = NULL;
+
+static int authbearer_initialised = 0;
+
+bool
+Auth::Bearer::Config::active() const
+{
+ return authbearer_initialised == 1;
+}
+
+bool
+Auth::Bearer::Config::configured() const
+{
+ if ((authenticateProgram != NULL) && (authenticateChildren.n_max != 0) &&
+ !realm.isEmpty() && (bearerAuthScope != NULL)) {
+ debugs(29, 9, "returning configured");
+ return true;
+ }
+
+ debugs(29, 9, "returning unconfigured");
+ return false;
+}
+
+const char *
+Auth::Bearer::Config::type() const
+{
+ return Auth::Bearer::Scheme::GetInstance()->type();
+}
+
+void
+Auth::Bearer::Config::fixHeader(Auth::UserRequest::Pointer, HttpReply *rep,
http_hdr_type hdrType, HttpRequest *)
+{
+ if (authenticateProgram) {
+ debugs(29, 9, "Sending type:" << hdrType << " header: 'Bearer
realm=\"" << realm << "\", scope=\"" << bearerAuthScope << "\"'");
+ httpHeaderPutStrf(&rep->header, hdrType, "Bearer realm=\"" SQUIDSBUFPH
"\", scope=\"%s\"", SQUIDSBUFPRINT(realm), bearerAuthScope);
+ }
+}
+
+void
+Auth::Bearer::Config::rotateHelpers()
+{
+ /* schedule closure of existing helpers */
+ if (bearerauthenticators) {
+ helperShutdown(bearerauthenticators);
+ }
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+
+/** shutdown the auth helpers and free any allocated configuration details */
+void
+Auth::Bearer::Config::done()
+{
+ Auth::Config::done();
+
+ authbearer_initialised = 0;
+
+ if (bearerauthenticators) {
+ helperShutdown(bearerauthenticators);
+ }
+
+ delete bearerauthenticators;
+ bearerauthenticators = NULL;
+
+ if (authenticateProgram)
+ wordlistDestroy(&authenticateProgram);
+
+ safe_free(bearerAuthScope);
+}
+
+void
+Auth::Bearer::Config::dump(StoreEntry * entry, const char *name, Auth::Config
* scheme)
+{
+ if (!Auth::Config::dump(entry, name, scheme))
+ return false; // not configured
+
+ storeAppendPrintf(entry, "%s bearer scope %s\n", name, bearerAuthScope);
+ storeAppendPrintf(entry, "%s bearer cleanup_interval %" PRId64 "
seconds\n", name, static_cast<int64_t>(tokenGCInterval));
+}
+
+Auth::Bearer::Config::Config()
+{
+ static const SBuf defaultRealm("Squid proxy-caching web server");
+ realm = defaultRealm;
+ bearerAuthScope = xstrdup("proxy:HTTP");
+}
+
+Auth::Bearer::Config::~Config()
+{
+ safe_free(bearerAuthScope);
+}
+
+void
+Auth::Bearer::Config::parse(Auth::Config * scheme, int n_configured, char
*param_str)
+{
+ if (strcmp(param_str, "scope") == 0) {
+ parse_eol(&bearerAuthScope);
+ } else if (strcmp(param_str, "cleanup_interval") == 0) {
+ parse_time_t(&tokenGCInterval);
+ } else
+ Auth::Config::parse(scheme, n_configured, param_str);
+}
+
+static void
+authenticateBearerStats(StoreEntry * sentry)
+{
+ helperStats(sentry, bearerauthenticators, "Bearer Authenticator
Statistics");
+}
+
+/**
+ * Decode a Bearer [Proxy-]Auth string. Looking for an existing
+ * Auth::UserRequest structure with matching token, or create a
+ * new one if needed.
+ *
+ * Note that just returning will be treated as
+ * "cannot decode credentials". Use the message field to return
+ * a descriptive message to the user.
+ */
+Auth::UserRequest::Pointer
+Auth::Bearer::Config::decode(char const *proxy_auth, const char *aRequestRealm)
+{
+ Auth::UserRequest::Pointer auth_user_request =
dynamic_cast<Auth::UserRequest*>(new Auth::Bearer::UserRequest);
+
+ // retrieve the b68token
+ SBuf blob(proxy_auth);
+ if (!blob.isEmpty()) {
+ // trim prefix: WSP "Bearer" WSP
+ SBuf::size_type p = blob.findFirstNotOf(CharacterSet::WSP);
+ blob.consume(p);
+ p = blob.findFirstOf(CharacterSet::WSP);
+ blob.consume(p);
+ p = blob.findFirstNotOf(CharacterSet::WSP);
+ blob.consume(p);
+ }
+
+ // empty header? no auth details produced...
+ if (blob.isEmpty())
+ return auth_user_request;
+
+ // XXX: if the token contains non-b68token characters it is invalid.
+
+ Auth::User::Pointer auth_user;
+
+ // if there is a cached entry for this token use it
+ TokenCache::iterator itr = Auth::Bearer::Token::Cache.find(SBuf(blob));
+ if (!itr->second) {
+ // generate a User object for this token and cache it
+ Auth::Bearer::User *usr = new Auth::Bearer::User(this, aRequestRealm);
+ usr->token = new Token(blob);
+ usr->token->user = usr;
+ usr->auth_type = Auth::AUTH_BEARER;
+
+ Auth::Bearer::Token::Cache.insert(std::pair<const
SBuf,Auth::Bearer::TokenPointer>(SBuf(blob), usr->token));
+ auth_user = usr;
+
+ } else
+ auth_user = itr->second->user;
+
+ debugs(29, 3, "Found Bearer token " << blob <<" , user=" <<
auth_user->username() << ", " << auth_user);
+
+ assert(auth_user != NULL);
+ auth_user_request->user(auth_user);
+ return auth_user_request;
+}
+
+/**
+ * Initialize helpers and the like for this auth scheme.
+ * Called AFTER parsing the config file
+ */
+void
+Auth::Bearer::Config::init(Auth::Config * schemeCfg)
+{
+ if (authenticateProgram && !authbearer_initialised) {
+ authbearer_initialised = 1;
+
+ if (bearerauthenticators == NULL)
+ bearerauthenticators = new helper("bearerauthenticator");
+
+ bearerauthenticators->cmdline = authenticateProgram;
+
+ bearerauthenticators->childs.updateLimits(authenticateChildren);
+
+ bearerauthenticators->ipc_type = IPC_STREAM;
+
+ helperOpenServers(bearerauthenticators);
+
+ TokenCacheCleanup(NULL); // kick-start garbage collection events
+ }
+}
+
+void
+Auth::Bearer::Config::registerWithCacheManager(void)
+{
+ Mgr::RegisterAction("bearerauthenticator",
+ "Bearer User Authenticator Stats",
+ authenticateBearerStats, 0, 1);
+}
+
+void
+Auth::Bearer::Config::TokenCacheCleanup(void *)
+{
+ debugs(29, 3, "Cleaning the token cache at time=" << current_time.tv_sec);
+
+ // only clear tokens out of cache after 1 hour stale
+ // could make this configurable
+ time_t stalenessLimit = current_time.tv_sec - 60*60;
+
+ /*
+ * For big caches we could consider stepping through
+ * 100/200 entries at a time. Lets see how it flies
+ * first.
+ */
+ Auth::Bearer::TokenCache *cache = &Auth::Bearer::Token::Cache;
+ for (Auth::Bearer::TokenCache::iterator itr = cache->begin(); itr !=
cache->end();) {
+ debugs(29, 3, "token " << itr->second->b68encoded
+ << ", user=" << itr->second->user->username()
+ << ", expires=" << itr->second->expires);
+
+ // remove if it is too old
+ if (itr->second->expires < stalenessLimit) {
+ debugs(29, 3, "token " << itr->second->b68encoded << " removed");
+ Auth::Bearer::TokenCache::iterator remove = itr++;
+ cache->erase(remove);
+ } else
+ ++itr;
+ }
+
+ debugs(29, 3, "Finished cleaning the token cache.");
+
+ const Auth::Bearer::Config *cfg =
static_cast<Auth::Bearer::Config*>(Auth::Config::Find("bearer"));
+ if (cfg && cfg->active())
+ eventAdd("Bearer token cache maintenance",
Auth::Bearer::Config::TokenCacheCleanup, NULL, cfg->tokenGCInterval, 1);
+}
=== added file 'src/auth/bearer/Config.h'
--- src/auth/bearer/Config.h 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Config.h 2014-07-31 00:17:41 +0000
@@ -0,0 +1,48 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_CONFIG_H
+#define _SQUID_SRC_AUTH_BEARER_CONFIG_H
+
+#include "auth/bearer/forward.h"
+#include "auth/Config.h"
+#include "auth/Gadgets.h"
+#include "auth/UserRequest.h"
+#include "helper.h"
+
+namespace Auth
+{
+namespace Bearer
+{
+
+/// Bearer authentication configuration data
+class Config : public Auth::Config
+{
+public:
+ Config();
+ ~Config();
+ virtual bool active() const;
+ virtual bool configured() const;
+ virtual Auth::UserRequest::Pointer decode(char const *proxy_auth, const
char *requestRealm);
+ virtual void done();
+ virtual void rotateHelpers();
+ virtual void dump(StoreEntry *, const char *, Auth::Config *);
+ virtual void fixHeader(Auth::UserRequest::Pointer, HttpReply *,
http_hdr_type, HttpRequest *);
+ virtual void init(Auth::Config *);
+ virtual void parse(Auth::Config *, int, char *);
+ void decode(char const *httpAuthHeader, Auth::UserRequest::Pointer);
+ virtual void registerWithCacheManager(void);
+ virtual const char * type() const;
+
+public:
+ char *bearerAuthScope;
+ time_t tokenGCInterval;
+
+private:
+ void tokenCacheSetup();
+ static void TokenCacheCleanup(void *);
+};
+
+} // namespace Bearer
+} // namespace Auth
+
+extern helper *bearerauthenticators;
+
+#endif /* _SQUID_SRC_AUTH_BEARER_CONFIG_H */
=== added file 'src/auth/bearer/Makefile.am'
--- src/auth/bearer/Makefile.am 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Makefile.am 2014-07-30 12:25:52 +0000
@@ -0,0 +1,17 @@
+include $(top_srcdir)/src/Common.am
+include $(top_srcdir)/src/TestHeaders.am
+
+noinst_LTLIBRARIES = libbearer.la
+
+libbearer_la_SOURCES = \
+ Config.cc \
+ Config.h \
+ forward.h \
+ Scheme.cc \
+ Scheme.h \
+ Token.cc \
+ Token.h \
+ User.cc \
+ User.h \
+ UserRequest.cc \
+ UserRequest.h
=== added file 'src/auth/bearer/Scheme.cc'
--- src/auth/bearer/Scheme.cc 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Scheme.cc 2014-05-22 09:21:34 +0000
@@ -0,0 +1,40 @@
+#include "squid.h"
+#include "auth/bearer/Config.h"
+#include "auth/bearer/Scheme.h"
+#include "Debug.h"
+#include "helper.h"
+
+Auth::Scheme::Pointer Auth::Bearer::Scheme::_instance = NULL;
+
+Auth::Scheme::Pointer
+Auth::Bearer::Scheme::GetInstance()
+{
+ if (_instance == NULL) {
+ _instance = new Auth::Bearer::Scheme();
+ AddScheme(_instance);
+ }
+ return _instance;
+}
+
+char const *
+Auth::Bearer::Scheme::type() const
+{
+ return "bearer";
+}
+
+void
+Auth::Bearer::Scheme::shutdownCleanup()
+{
+ if (_instance == NULL)
+ return;
+
+ _instance = NULL;
+ debugs(29, DBG_CRITICAL, "Shutdown: " << type() << " authentication.");
+}
+
+Auth::Config *
+Auth::Bearer::Scheme::createConfig()
+{
+ Auth::Bearer::Config *newCfg = new Auth::Bearer::Config;
+ return dynamic_cast<Auth::Config*>(newCfg);
+}
=== added file 'src/auth/bearer/Scheme.h'
--- src/auth/bearer/Scheme.h 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Scheme.h 2014-05-27 01:41:29 +0000
@@ -0,0 +1,36 @@
+#ifndef SQUID_AUTH_BEARER_SCHEME_H
+#define SQUID_AUTH_BEARER_SCHEME_H
+
+#include "auth/Scheme.h"
+
+namespace Auth
+{
+namespace Bearer
+{
+
+/// scheme instance for OAuth 2.0 Bearer
+class Scheme : public Auth::Scheme
+{
+
+public:
+ static Auth::Scheme::Pointer GetInstance();
+ Scheme() {};
+ virtual ~Scheme() {}
+
+ /* per scheme */
+ virtual char const *type() const;
+ virtual void shutdownCleanup();
+ virtual Auth::Config *createConfig();
+
+private:
+ /* Not implemented */
+ Scheme(Scheme const &);
+ Scheme &operator=(Scheme const &);
+
+ static Auth::Scheme::Pointer _instance;
+};
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* SQUID_AUTH_BEARER_SCHEME_H */
=== added file 'src/auth/bearer/Token.cc'
--- src/auth/bearer/Token.cc 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Token.cc 2014-05-27 05:56:21 +0000
@@ -0,0 +1,9 @@
+#include "squid.h"
+#include "auth/bearer/Token.h"
+
+Auth::Bearer::TokenCache Auth::Bearer::Token::Cache;
+
+Auth::Bearer::Token::~Token()
+{
+ // TODO detatch this token from the Auth:User ??
+}
=== added file 'src/auth/bearer/Token.h'
--- src/auth/bearer/Token.h 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Token.h 2014-05-28 11:06:21 +0000
@@ -0,0 +1,43 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_TOKEN_H
+#define _SQUID_SRC_AUTH_BEARER_TOKEN_H
+
+#include "auth/bearer/forward.h"
+#include "base/RefCount.h"
+#include "SquidTime.h"
+
+namespace Auth {
+namespace Bearer {
+
+/// a Bearer token we have seen and details we associate with it
+class Token : public RefCountable
+{
+public:
+ MEMPROXY_CLASS(Auth::Bearer::Token);
+
+ Token() : user(NULL), expires(squid_curtime) {}
+ explicit Token(const SBuf &token) : b68encoded(token), user(NULL),
expires(squid_curtime) {}
+ ~Token();
+
+ /// the B68 encoding form of this token
+ SBuf b68encoded;
+
+ /// the Auth::User this token is tied to
+ Auth::Bearer::User *user;
+
+ /// when this token will expire
+ time_t expires;
+
+ /// a cache of known tokens
+ static TokenCache Cache;
+
+private:
+ Token(const Token &); // do not implement
+ Token &operator =(const Token&); // do not implement
+};
+
+MEMPROXY_CLASS_INLINE(Auth::Bearer::Token);
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* _SQUID_SRC_AUTH_BEARER_TOKEN_H */
=== added file 'src/auth/bearer/User.cc'
--- src/auth/bearer/User.cc 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/User.cc 2014-05-28 11:49:31 +0000
@@ -0,0 +1,28 @@
+#include "squid.h"
+#include "auth/bearer/User.h"
+#include "auth/bearer/UserRequest.h"
+#include "auth/Config.h"
+#include "auth/QueueNode.h"
+#include "Debug.h"
+
+Auth::Bearer::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
+ Auth::User(aConfig, aRequestRealm),
+ queue(NULL)
+{
+}
+
+Auth::Bearer::User::~User()
+{
+ debugs(29, 5, "doing nothing to clear Bearer scheme data for " << this);
+}
+
+int32_t
+Auth::Bearer::User::ttl() const
+{
+ // no token? should never happen, but treat as expired
+ if (token == NULL)
+ return -1;
+
+ // credentials expiry depends on the Token age.
+ return static_cast<int32_t>(token->expires - squid_curtime);
+}
=== added file 'src/auth/bearer/User.h'
--- src/auth/bearer/User.h 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/User.h 2014-05-27 01:45:19 +0000
@@ -0,0 +1,38 @@
+#ifndef _SQUID_AUTH_BEARER_USER_H
+#define _SQUID_AUTH_BEARER_USER_H
+
+#include "auth/bearer/Token.h"
+#include "auth/User.h"
+
+namespace Auth
+{
+
+class QueueNode;
+
+namespace Bearer
+{
+
+/// User credentials for the Bearer authentication protocol
+class User : public Auth::User
+{
+public:
+ MEMPROXY_CLASS(Auth::Bearer::User);
+
+ User(Auth::Config *, const char *requestRealm);
+ ~User();
+
+ virtual int32_t ttl() const;
+
+ /// authentication attempts waiting on helper feedback
+ QueueNode *queue;
+
+ /// the token used to create this User object
+ TokenPointer token;
+};
+
+MEMPROXY_CLASS_INLINE(Auth::Bearer::User);
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* _SQUID_AUTH_BEARER_USER_H */
=== added file 'src/auth/bearer/UserRequest.cc'
--- src/auth/bearer/UserRequest.cc 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/UserRequest.cc 2014-05-28 11:48:22 +0000
@@ -0,0 +1,251 @@
+#include "squid.h"
+#include "auth/bearer/Config.h"
+#include "auth/bearer/User.h"
+#include "auth/bearer/UserRequest.h"
+#include "auth/QueueNode.h"
+#include "auth/State.h"
+#include "cbdata.h"
+#include "Debug.h"
+
+Auth::Bearer::UserRequest::UserRequest()
+{
+}
+
+Auth::Bearer::UserRequest::~UserRequest()
+{
+}
+
+int
+Auth::Bearer::UserRequest::authenticated() const
+{
+ if (user() != NULL && user()->credentials() == Auth::Ok) {
+ debugs(29, 9, "user authenticated");
+ return 1;
+ }
+
+ debugs(29, 9, "user not fully authenticated");
+ return 0;
+}
+
+const char *
+Auth::Bearer::UserRequest::credentialsStr()
+{
+ static char buf[MAX_AUTHTOKEN_LEN];
+ buf[0] = '\0';
+
+ Auth::Bearer::User const *usr = dynamic_cast<Auth::Bearer::User const
*>(user().getRaw());
+ if (usr)
+ snprintf(buf, sizeof(buf), SQUIDSBUFPH "\n",
SQUIDSBUFPRINT(usr->token->b68encoded));
+ return buf;
+}
+
+Auth::Direction
+Auth::Bearer::UserRequest::module_direction()
+{
+ // null auth_user is checked for by Auth::UserRequest::direction()
+
+ if (user()->auth_type != Auth::AUTH_BEARER)
+ return Auth::CRED_ERROR;
+
+ switch (user()->credentials()) {
+
+ case Auth::Unchecked:
+ case Auth::Pending:
+ return Auth::CRED_LOOKUP;
+
+ case Auth::Ok:
+ if (user()->ttl() <= 0)
+ return Auth::CRED_LOOKUP;
+ return Auth::CRED_VALID;
+
+ case Auth::Failed:
+ return Auth::CRED_VALID;
+
+ default:
+ debugs(29, DBG_IMPORTANT, "WARNING: Bearer Authentication in
unexpected state: " << user()->credentials());
+ return Auth::CRED_ERROR;
+ }
+}
+
+void
+Auth::Bearer::UserRequest::startHelperLookup(HttpRequest *req,
AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
+{
+ assert(data);
+ assert(handler);
+
+ assert(user() != NULL);
+ assert(user()->auth_type == Auth::AUTH_BEARER);
+
+ if
(static_cast<Auth::Bearer::Config*>(Auth::Config::Find("bearer"))->authenticateProgram
== NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Bearer authentication program
configured.");
+ handler(data);
+ return;
+ }
+
+ Auth::Bearer::User *bearer_auth = dynamic_cast<Auth::Bearer::User
*>(user().getRaw());
+ assert(bearer_auth != NULL);
+
+ // check to see if the user already has a request outstanding
+ if (user()->credentials() == Auth::Pending) {
+
+ debugs(29, 8, "token " << bearer_auth->token->b68encoded << " queue
after lookup already underway");
+ // there is a request with the same credentials already being verified
+ Auth::QueueNode *node = new Auth::QueueNode(this, handler, data);
+
+ // queue this validation request to be informed of the pending lookup
results
+ node->next = bearer_auth->queue;
+ bearer_auth->queue = node;
+ return;
+ }
+ // otherwise submit this request to the auth helper(s) for validation
+
+ debugs(29, 8, "credentials state is " << user()->credentials());
+
+ static char buf[MAX_AUTHTOKEN_LEN];
+ int sz;
+ if (const char *keyExtras = helperRequestKeyExtras(req, al))
+ sz = snprintf(buf, sizeof(buf), SQUIDSBUFPH " %s\n",
SQUIDSBUFPRINT(bearer_auth->token->b68encoded), keyExtras);
+ else
+ sz = snprintf(buf, sizeof(buf), SQUIDSBUFPH "\n",
SQUIDSBUFPRINT(bearer_auth->token->b68encoded));
+
+ if (sz<=0) {
+ debugs(9, DBG_CRITICAL, "ERROR: Bearer Authentication Failure. Can not
build helper validation request.");
+ handler(data);
+ } else if (static_cast<size_t>(sz) >= sizeof(buf)) {
+ debugs(9, DBG_CRITICAL, "ERROR: Bearer Authentication Failure. Helper
request line exceeds " << sizeof(buf) << " bytes.");
+ handler(data);
+ } else {
+ user()->credentials(Auth::Pending);
+ debugs(29, 3, "token " << bearer_auth->token->b68encoded << " lookup
started");
+ helperSubmit(bearerauthenticators, buf,
Auth::Bearer::UserRequest::HandleReply,
+ new Auth::StateData(this, handler, data));
+ }
+}
+
+void
+Auth::Bearer::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData
* conn, http_hdr_type type)
+{
+ assert(user() != NULL);
+
+ // if the password is not ok, do an identity
+ if (!user() || user()->credentials() != Auth::Ok)
+ return;
+
+ // are we about to recheck the credentials externally?
+ if (user()->ttl() <= 0) {
+ debugs(29, 4, "credentials expired - rechecking");
+ user()->credentials(Auth::Unchecked);
+ return;
+ }
+
+ // we have been through the external helper, and the credentials haven't
expired
+ debugs(29, 9, "user " << user()->username() << " authenticated");
+}
+
+void
+Auth::Bearer::UserRequest::HandleReply(void *data, const HelperReply &reply)
+{
+ Auth::StateData *r = static_cast<Auth::StateData *>(data);
+
+ debugs(29, 8, "helper: " << reply.whichServer << " sent us reply=" <<
reply);
+
+ if (!cbdataReferenceValid(r->data)) {
+ debugs(29, DBG_IMPORTANT, "ERROR: Bearer Authentication invalid
callback data.");
+ delete r;
+ return;
+ }
+
+ Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+
+ // add new helper kv-pair notes to the credentials object
+ // so that any transaction using those credentials can access them
+ auth_user_request->user()->notes.appendNewOnly(&reply.notes);
+
+ assert(auth_user_request->user() != NULL);
+ assert(auth_user_request->user()->auth_type == Auth::AUTH_BEARER);
+
+ switch (reply.result) {
+ case HelperReply::Okay: {
+ const char *userNote = reply.notes.findFirst("user");
+ const char *ttlNote = reply.notes.findFirst("ttl");
+ if (userNote == NULL) {
+ /* protocol error */
+ fatalf("Auth::Bearer::HandleReply: *** Unsupported helper response
***, '%s'\n", reply.other().content());
+ break;
+ }
+
+ Auth::Bearer::User *usr = dynamic_cast<Auth::Bearer::User
*>(auth_user_request->user().getRaw());
+ /* we're finished, user is authenticated */
+ usr->username(userNote);
+ auth_user_request->denyMessage("Login successful");
+ const int64_t ttl = (ttlNote!= NULL) ? strtoll(ttlNote, NULL, 10) : -1;
+ usr->token->expires = current_time.tv_sec + ttl;
+ usr->expiretime = max(usr->expiretime, usr->token->expires);
+ usr->credentials(Auth::Ok);
+ usr->addToNameCache();
+ debugs(29, 4, "Successfully validated user via Bearer. Username " <<
auth_user_request->user()->username());
+ }
+ break;
+
+ case HelperReply::Error: {
+ const char *messageNote = reply.notes.find("message");
+ const char *ttlNote = reply.notes.findFirst("ttl");
+
+ /* authentication failure (wrong password, etc.) */
+ if (messageNote != NULL)
+ auth_user_request->denyMessage(messageNote);
+ else
+ auth_user_request->denyMessage("Bearer Authentication denied with
no reason given");
+
+ Auth::Bearer::User *usr = dynamic_cast<Auth::Bearer::User
*>(auth_user_request->user().getRaw());
+
+ const int64_t ttl = (ttlNote!= NULL) ? strtoll(ttlNote, NULL, 10) : -1;
+ usr->token->expires = current_time.tv_sec + ttl;
+ usr->expiretime = max(usr->expiretime, usr->token->expires);
+ usr->credentials(Auth::Failed);
+ debugs(29, 4, "Failed validating user via Bearer. Result: " << reply);
+ }
+ break;
+
+ case HelperReply::Unknown:
+ debugs(29, DBG_IMPORTANT, "ERROR: Bearer Authentication Helper '" <<
reply.whichServer << "' crashed!.");
+ /* continue to the next case */
+
+ case HelperReply::TT:
+ /* continue to the next case */
+
+ case HelperReply::BrokenHelper: {
+ const char *errNote = reply.notes.find("message");
+ if (reply.result == HelperReply::Unknown)
+ auth_user_request->denyMessage("Internal Error");
+ else if (errNote != NULL)
+ auth_user_request->denyMessage(errNote);
+ else
+ auth_user_request->denyMessage("Bearer Authentication failed with
no reason given");
+ auth_user_request->user()->credentials(Auth::Failed);
+ debugs(29, DBG_IMPORTANT, "ERROR: Bearer Authentication validating
user. Result: " << reply);
+ } // break;
+ }
+
+ // Notify all waiting transactions of the result
+ void *cbdata;
+ if (cbdataReferenceValidDone(r->data, &cbdata))
+ r->handler(cbdata);
+
+ cbdataReferenceDone(r->data);
+
+ Auth::Bearer::User *local_usr = dynamic_cast<Auth::Bearer::User
*>(auth_user_request->user().getRaw());
+ while (local_usr->queue) {
+ if (cbdataReferenceValidDone(local_usr->queue->data, &cbdata))
+ local_usr->queue->handler(cbdata);
+
+ Auth::QueueNode *tmpnode = local_usr->queue->next;
+ local_usr->queue->next = NULL;
+ delete local_usr->queue;
+
+ local_usr->queue = tmpnode;
+ }
+
+ delete r;
+}
=== added file 'src/auth/bearer/UserRequest.h'
--- src/auth/bearer/UserRequest.h 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/UserRequest.h 2014-05-28 11:48:12 +0000
@@ -0,0 +1,38 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_USERREQUEST_H
+#define _SQUID_SRC_AUTH_BEARER_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+
+namespace Auth
+{
+namespace Bearer
+{
+
+class UserRequest : public Auth::UserRequest
+{
+public:
+ MEMPROXY_CLASS(Auth::Bearer::UserRequest);
+
+ UserRequest();
+ virtual ~UserRequest();
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn,
http_hdr_type type);
+ virtual Direction module_direction();
+ virtual void startHelperLookup(HttpRequest *request,
AccessLogEntry::Pointer &al, AUTHCB *, void *);
+ virtual const char *credentialsStr();
+
+private:
+ static HLPCB HandleReply;
+};
+
+} // namespace Bearer
+} // namespace Auth
+
+MEMPROXY_CLASS_INLINE(Auth::Bearer::UserRequest);
+
+#endif /* _SQUID_SRC_AUTH_BEARER_USERREQUEST_H */
=== added file 'src/auth/bearer/forward.h'
--- src/auth/bearer/forward.h 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/forward.h 2014-05-28 08:11:03 +0000
@@ -0,0 +1,29 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_FORWARD_H
+#define _SQUID_SRC_AUTH_BEARER_FORWARD_H
+
+#include "base/RefCount.h"
+#include "SBuf.h"
+
+#include <map>
+
+namespace Auth {
+
+/** OAuth 2.0 Bearer token Authentication in HTTP
+ *
+ * RFC 6750 OAuth 2.0 Authorization Framework: Bearer Token Usage
+ * http://tools.ietf.org/rfc/rfc6750
+ */
+namespace Bearer {
+
+class Token;
+class User;
+class UserRequest;
+
+typedef RefCount<Auth::Bearer::Token> TokenPointer;
+
+typedef std::map<const SBuf, TokenPointer> TokenCache;
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* _SQUID_SRC_AUTH_BEARER_FORWARD_H */
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2014-07-30 15:31:10 +0000
+++ src/cf.data.pre 2014-07-31 06:12:39 +0000
@@ -352,41 +352,41 @@
The expanded key_extras value is added to the Squid credentials
cache and, hence, will affect authentication. It can be used to
autenticate different users with identical user names (e.g.,
when user authentication depends on http_port).
Avoid adding frequently changing information to key_extras. For
example, if you add user source IP, and it changes frequently
in your environment, then max_user_ip ACL is going to treat
every user+IP combination as a unique "user", breaking the ACL
and wasting a lot of memory on those user records. It will also
force users to authenticate from scratch whenever their IP
changes.
"realm" string
Specifies the protection scope (aka realm name) which is to be
reported to the client for the authentication scheme. It is
commonly part of the text the user will see when prompted for
their username and password.
- For Basic the default is "Squid proxy-caching web server".
+ For Basic and Bearer the default is "Squid proxy-caching web
server".
For Digest there is no default, this parameter is mandatory.
For NTLM and Negotiate this parameter is ignored.
"children" numberofchildren [startup=N] [idle=N] [concurrency=N]
The maximum number of authenticator processes to spawn. If
you start too few Squid will have to wait for them to process
a backlog of credential verifications, slowing it down. When
password verifications are done via a (slow) network you are
likely to need lots of authenticator processes.
The startup= and idle= options permit some skew in the exact
amount run. A minimum of startup=N will begin during startup
and reconfigure. Squid will start more in groups of up to
idle=N in an attempt to meet traffic needs and to keep idle=N
free above those traffic needs up to the maximum.
The concurrency= option sets the number of concurrent requests
the helper can process. The default of 0 is used for helpers
who only supports one request at a time. Setting this to a
@@ -414,40 +414,55 @@
"credentialsttl" timetolive
Specifies how long squid assumes an externally validated
username:password pair is valid for - in other words how
often the helper program is called for that user. Set this
low to force revalidation with short lived passwords.
NOTE: setting this high does not impact your susceptibility
to replay attacks unless you are using an one-time password
system (such as SecureID). If you are using such a system,
you will be vulnerable to replay attacks unless you also
use the max_user_ip ACL in an http_access rule.
"casesensitive" on|off
Specifies if usernames are case sensitive. Most user databases
are case insensitive allowing the same username to be spelled
using both lower and upper case letters, but some are case
sensitive. This makes a big difference for user_max_ip ACL
processing and similar.
ENDIF
+IF HAVE_AUTH_MODULE_BEARER
+ === Bearer authentication parameters ===
+
+ "scope" scopestring
+ Specifies the scope name(s) which are to be reported to the
+ client for the Bearer proxy authentication scheme.
+ The default is: proxy:HTTP
+
+ "cleanup_interval" timeinterval
+ Specifies the interval for garbage collection events which
+ remove excessively stale Bearer tokens. Tokens are deemed
+ excessively stale if they have not been seen for an hour or
+ more since their expiry time.
+
+ENDIF
IF HAVE_AUTH_MODULE_DIGEST
=== Digest authentication parameters ===
"utf8" on|off
HTTP uses iso-latin-1 as character set, while some
authentication backends such as LDAP expects UTF-8. If this is
set to on Squid will translate the HTTP iso-latin-1 charset to
UTF-8 before sending the username and password to the helper.
"nonce_garbage_interval" timeinterval
Specifies the interval that nonces that have been issued
to client_agent's are checked for validity.
"nonce_max_duration" timeinterval
Specifies the maximum length of time a given nonce will be
valid for.
"nonce_max_count" number
Specifies the maximum number of times a given nonce can be
used.
@@ -492,40 +507,43 @@
are supported by the proxy.
ENDIF
=== Example Configuration ===
This configuration displays the recommended authentication scheme
order from most to least secure with recommended minimum configuration
settings for each scheme:
#auth_param negotiate program <uncomment and complete this line to activate>
#auth_param negotiate children 20 startup=0 idle=1
#auth_param negotiate keep_alive on
#
#auth_param digest program <uncomment and complete this line to activate>
#auth_param digest children 20 startup=0 idle=1
#auth_param digest realm Squid proxy-caching web server
#auth_param digest nonce_garbage_interval 5 minutes
#auth_param digest nonce_max_duration 30 minutes
#auth_param digest nonce_max_count 50
#
+#auth_param bearer program <uncomment and complete this line>
+#auth_param bearer scope proxy:squid
+#
#auth_param ntlm program <uncomment and complete this line to activate>
#auth_param ntlm children 20 startup=0 idle=1
#auth_param ntlm keep_alive on
#
#auth_param basic program <uncomment and complete this line>
#auth_param basic children 5 startup=5 idle=1
#auth_param basic realm Squid proxy-caching web server
#auth_param basic credentialsttl 2 hours
DOC_END
NAME: authenticate_cache_garbage_interval
TYPE: time_t
DEFAULT: 1 hour
LOC: Config.authenticateGCInterval
DOC_START
The time period between garbage collection across the username cache.
This is a trade-off between memory utilization (long intervals - say
2 days) and CPU (short intervals - say 1 minute). Only change if you
have good reason to.
DOC_END