Commit c699a271 authored by Moritz Schlarb's avatar Moritz Schlarb

New upstream version 2.2.1rc4

parent 98bdf6fe
<!--
NOTE WELL
A new issue should be about a bug or a feature!
A question should go to the mailinglist at:
mod_auth_openidc@googlegroups.com
The corresponding forum/archive is at:
https://groups.google.com/forum/#!forum/mod_auth_openidc
-->
### Expected behaviour
### Actual behaviour
### Minimized example that reproduces the behaviour
/aclocal.m4
/.cproject
/.project
/config.log
/config.status
/configure
/Makefile
/discover
/metadata
/.settings
language: c
dist: trusty
addons:
apt:
packages:
- libssl-dev
- libcurl4-openssl-dev
- libjansson-dev
- libhiredis-dev
- libpcre3-dev
- apache2-dev
- check
install:
- wget https://github.com/cisco/cjose/archive/0.4.1.tar.gz
- tar zxvf 0.4.1.tar.gz
- cd cjose-0.4.1
- ./configure --prefix=/usr
- make test
- sudo make install
- cd -
script:
- ./autogen.sh && ./configure --with-apxs2=/usr/bin/apxs2 CFLAGS=-Werror && make && make test
......@@ -33,3 +33,5 @@ reporting bugs, providing fixes, suggesting useful features or other:
drdivano <https://github.com/drdivano>
AliceWonderMiscreations <https://github.com/AliceWonderMiscreations>
Wouter Hund <https://github.com/wouterhund>
Hans Keeler <https://github.com/hkeeler>
Moritz Schlarb <https://github.com/moschlar>
04/20/2017
- support relative RedirectURIs; closes #200; thanks @moschlar
- bump to 2.2.1rc4
04/08/2017
- fix potential crash on prefork process exit when used with Redis cache backend (3x)
- bump to 2.2.1rc3
04/06/2017
- change warn log about missing token binding ID to debug log
04/05/2017
- allow for high session inactivity timeout max value
- improve error message in oidc_util_http_send when ap_pass_brigade fails and mention possible interference with mod_deflate
- bump to 2.2.1rc0
03/30/2017
- merge feature branch back to master:
- better support for Single Page Applications, see:
https://github.com/pingidentity/mod_auth_openidc/wiki/Single-Page-Applications
- add session info hook that is configurable through OIDCInfoHook
- add "AuthType auth-openidc" option that allows both "oauth20" and "openid-connect" on the same path
- add encryption for all cache entries instead of just session data through OIDCCacheEncrypt
- add cookie SameSite flag/policy through OIDCCookieSameSite
- return HTTP 200 on OPTIONS requests to (unauthenticated) "oauth20" paths
- add fallback to a by-value session cookie if the primary session cache fails with OIDCSessionCacheFallbackToCookie
- add support for black- and/or white-listing claims with OIDCBlackListedClaims and OIDCWhiteListedClaims
- add prototype token binding support in conjunction with:
https://github.com/zmartzone/mod_token_binding:
- for state & session cookies, see:
https://github.com/TokenBinding/Internet-Drafts
- for ID tokens with OpenID Connect Token Bound Authentication support, see:
http://openid.net/specs/openid-connect-token-bound-authentication-1_0.html
- for Authorization Codes with OAuth 2.0 Token Binding for Authorization Codes, see:
https://tools.ietf.org/html/draft-ietf-oauth-token-binding
- refactoring:
- refactor session state, proto state and headers into getters/setters functions
- refactor PKCE support
- fix removing session state from cache on logout
- fix clearing chunked session cookies on logout; closes #246; thanks @Jharmuth
- release 2.2.0
02/20/2017
- security fix: scrub headers for "AuthType oauth20"
- release 2.1.6
......
......@@ -3,7 +3,7 @@ SRC=src/mod_auth_openidc.c \
src/cache/file.c \
src/cache/memcache.c \
src/cache/shm.c \
src/cache/lock.c \
src/cache/common.c \
src/oauth.c \
src/proto.c \
src/config.c \
......@@ -21,6 +21,11 @@ REDIS_CFLAGS=-DUSE_LIBHIREDIS @HIREDIS_CFLAGS@
REDIS_LIBS=@HIREDIS_LIBS@
endif
ifeq (@HAVE_LIBJQ@, 1)
JQ_CFLAGS=-DUSE_LIBJQ @JQ_CFLAGS@
JQ_LIBS=@JQ_LIBS@
endif
HDRS = \
$(JWT_HDRS) \
src/mod_auth_openidc.h \
......@@ -48,8 +53,8 @@ DISTFILES=$(SRC) \
all: src/mod_auth_openidc.la
CFLAGS=@OPENSSL_CFLAGS@ @CURL_CFLAGS@ @JANSSON_CFLAGS@ @CJOSE_CFLAGS@ @PCRE_CFLAGS@ $(REDIS_CFLAGS)
LIBS=@OPENSSL_LIBS@ @CURL_LIBS@ @JANSSON_LIBS@ @CJOSE_LIBS@ @PCRE_LIBS@ $(REDIS_LIBS)
CFLAGS=@OPENSSL_CFLAGS@ @CURL_CFLAGS@ @JANSSON_CFLAGS@ @CJOSE_CFLAGS@ @PCRE_CFLAGS@ $(REDIS_CFLAGS) $(JQ_CFLAGS)
LIBS=@OPENSSL_LIBS@ @CURL_LIBS@ @JANSSON_LIBS@ @CJOSE_LIBS@ @PCRE_LIBS@ $(REDIS_LIBS) $(JQ_LIBS)
src/mod_auth_openidc.la: $(SRC) $(HDRS)
@APXS2@ @APXS2_OPTS@ -Wc,"-DNAMEVER=\"@NAMEVER@\" $(CFLAGS)" -Wl,"$(LIBS)" -Wc,-Wall -Wc,-g -c $(SRC)
......
......@@ -122,7 +122,7 @@ Note that this is not an OpenID Connect SSO scenario where users are authenticat
scenario where **mod_auth_openidc** is the OAuth 2.0 Resource Server instead of the RP/client. How the actual
client accessing the protected resources got its access token is not relevant to this Apache Resource Server setup.
###OpenID Connect SSO with multiple OpenID Connect Providers
### OpenID Connect SSO with multiple OpenID Connect Providers
Sample configuration for multiple OpenID Connect providers, which triggers OpenID
Connect Discovery first to find the user's OP.
......@@ -221,7 +221,7 @@ An additional **mod_auth_openidc** specific parameter named `auth_request_params
in, see the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki#13-how-can-i-add-custom-parameters-to-the-authorization-request)
for its usage.
###OpenID Connect SSO & OAuth 2.0 Access Control with PingFederate
### OpenID Connect SSO & OAuth 2.0 Access Control with PingFederate
Another example config for using PingFederate as your OpenID Connect OP and/or
OAuth 2.0 Authorization server, based on the OAuth 2.0 PlayGround 3.x default
......@@ -275,8 +275,9 @@ The corresponding forum/archive is at:
For commercial support and consultancy you can contact:
[info@zmartzone.eu](mailto:info@zmartzone.eu)
Any questions/issues should go to the mailing list, the Github issues tracker or the
primary author [hans.zandbelt@zmartzone.eu](mailto:hans.zandbelt@zmartzone.eu)
Any questions/issues should go to the mailing list or the
primary author [hans.zandbelt@zmartzone.eu](mailto:hans.zandbelt@zmartzone.eu).
The Github issues tracker should be used only for bugs reports and feature requests.
Disclaimer
----------
......
......@@ -8,6 +8,8 @@
# The redirect_uri for this OpenID Connect client; this is a vanity URL
# that must ONLY point to a path on your server protected by this module
# but it must NOT point to any actual content that needs to be served.
# You can use a relative URL like /protected/redirect_uri if you want to
# support multiple vhosts that belong to the same security domain in a dynamic way
#OIDCRedirectURI https://www.example.com/protected/redirect_uri
# (Mandatory)
......@@ -22,7 +24,8 @@
# (Optional)
# Specify the domain for which the "state" and "session" cookies will be set.
# This must match the OIDCRedirectURI and the URL on which you host your protected
# application. When not defined the default is the server hostname.
# application. When using a relative OIDCRedirectURI this setting should most probably empty.
# When not defined the default is the server hostname that is currently accessed.
#OIDCCookieDomain <cookie-domain>
# (Optional)
......@@ -188,7 +191,16 @@
# The PKCE method used (this serves as default value for discovered OPs too)
# When not defined PKCE is not used.
# NB: this can be overridden on a per-OP basis in the .conf file using the key: pkce_method
#OIDCPKCEMethod [plain|S256]
#OIDCPKCEMethod [plain|S256|referred_tb]
# (Optional)
# The OpenID Connect Bound Authentication policy used,
# see: http://openid.net/specs/openid-connect-token-bound-authentication-1_0.html
# "disabled": no referred token binding will be requested from the User Agent upon redirection to the OP
# "optional": referred token binding will be requested, the "cnf["tbh"]" claim is optional on return
# "required": referred token binding will be requested, the "cnf["tbh"]" claim must be present when the Client supports Token Binding
# "enforced": referred token binding will be requested, the "cnf["tbh"]" claim must be present and the User Agent must support Token Binding
#OIDCTokenBindingPolicy [disabled|optional|required|enforced]
########################################################################################
#
......@@ -331,10 +343,16 @@
# d) JWK sets that have been retrieved from jwk_uri's
# e) resolved OP metadata when using OIDCProviderMetadataUrl
# f) JWT ID claims (jti) when using OP-init-SSO
# g) temporary state associated with Request URI's
# must be one of \"shm\", \"memcache\", \"file\" or, if Redis support is compiled in, \"redis\"
# When not defined, "shm" (shared memory) is used.
#OIDCCacheType [shm|memcache|file[|redis]]
# (Optional)
# Indicate whether data in the cache backend should be encrypted.
# When not defined the default is "Off" for the "shm" backend and "On" for all other cache backends
#OIDCCacheEncrypt [On|Off]
# (Optional)
# When using OIDCCacheType "shm":
# Specifies the maximum number of name/value pair entries that can be cached.
......@@ -556,6 +574,15 @@
# When not defined the default is On.
#OIDCCookieHTTPOnly [On|Off]
# (Optional)
# Defines whether the SameSite flag will be set on cookies.
# When On the following will apply:
# state cookie: Lax
# session cookie: first time set Lax, updates (e.g. after inactivity timeout) Strict
# x_csrf discovery: Strict:
# When not defined the default is Off.
#OIDCCookieSameSite [On|Off]
# (Optional)
# The prefix to use when setting claims (openid-connect or oauth20) in the HTTP headers/environment variables.
# When not defined, the default "OIDC_CLAIM_" is used.
......@@ -630,6 +657,18 @@
# When not defined the default "server-cache" is used.
#OIDCSessionType server-cache[:persistent]|client-cookie[:persistent]
# (Optional)
# Fallback to "OIDCSessionType client-cookie" when "OIDCSessionType server-cache" is set and the primary
# cache mechanism (e.g. memcache or redis) fails. Note that this will come at a cost of:
# a) performance
# 1) since on each subsequent request the primary cache will still be polled and
# failback will happen as soon as the primary cache is available again
# 2) information other than sessions cannot be cached, e.g. resolved access tokens or metadata; see: OIDCCacheType
# b) security, since nonce's and jti's are not cached, see: OIDCCacheType
# c) (prototype) functionality, since request_uri's won't work anymore
# When not defined the default is "Off".
#OIDCSessionCacheFallbackToCookie [On|Off]
# (Optional)
# OpenID Connect session cookie chunk size.
# When using "OIDCSessionType client-cookie" the session cookie may become quite large if a lot of session
......@@ -703,3 +742,28 @@
# When not defined the default is 0 seconds, i.e. it is never refreshed.
# Also used in a single provider setup with OIDCProviderMetadatURL but 0 then means the default of 1 day.
#OIDCProviderMetadataRefreshInterval <seconds>
# (Optional)
# Define the data that will be returned upon calling the info hook (i.e. <redirect_uri>?info=json)
# iat (int) : Unix timestamp indicating when this data was created
# access_token (string) : the access token
# access_token_expires (int) : the Unix timestamp which is a hint about when the access token will expire (as indicated by the OP)
# id_token (object) : the claims presented in the ID token
# userinfo (object) : the claims resolved from the UserInfo endpoint
# refresh_token (string) : the refresh token (if returned by the OP)
# session (object) : (for debugging) mod_auth_openidc specific session data such as "remote user", "session expiry", "session id" and a "state" object
# When not defined the session hook will not return any data but a HTTP 404
#OIDCInfoHook [iat|access_token|access_token_expires|id_token|userinfo|refresh_token|session]+
# (Optional)
# Specify claims that should be removed from the userinfo and/or id_token before storing them in the session.
# Note that OIDCBlackListedClaims takes precedence over OIDCWhiteListedClaims
# When not defined no claims are blacklisted and all claims are stored except when OIDCWhiteListedClaims is used.
#OIDCBlackListedClaims [<claim>]+
# (Optional)
# Specify claims from the userinfo and/or id_token that should be stored in the session (all other claims will be discarded).
# Note that OIDCBlackListedClaims takes precedence over OIDCWhiteListedClaims
# When not defined no claims are whitelisted and all claims are stored except when blacklisted with OIDCBlackListedClaims.
#OIDCWhiteListedClaims [<claim>]+
This source diff could not be displayed because it is too large. You can view the blob instead.
AC_INIT([mod_auth_openidc],[2.1.6],[hans.zandbelt@zmartzone.eu])
AC_INIT([mod_auth_openidc],[2.2.1rc4],[hans.zandbelt@zmartzone.eu])
AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
......@@ -86,6 +86,33 @@ AC_SUBST(HAVE_LIBHIREDIS)
AC_SUBST(HIREDIS_CFLAGS)
AC_SUBST(HIREDIS_LIBS)
# JQ
HAVE_LIBJQ=0
#AC_ARG_WITH(jq,
# [ --with-jq=PATH location of your libjq installation])
#if test -n "$with_jq"
#then
# JQ_CFLAGS="-I$with_jq/include"
# JQ_LIBS="-L$with_jq/lib -ljq"
#else
# JQ_LIBS="-ljq"
#fi
#CPPFLAGS="$JQ_CFLAGS $CPPFLAGS"
#AC_CHECK_HEADERS([jq.h], , [HAVE_LIBJQ=0])
#LDFLAGS="$JQ_LIBS $LDFLAGS"
#AC_CHECK_LIB([jq], [jq_init], , [HAVE_LIBJQ=0])
#if test "x$have_jq" = "x0" ; then
# AC_MSG_WARN("cannot find library for -ljq.")
#fi
AC_SUBST(HAVE_LIBJQ)
AC_SUBST(JQ_CFLAGS)
AC_SUBST(JQ_LIBS)
# Create Makefile from Makefile.in
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
/*.lo
/*.o
/*.slo
/*.la
/.libs
......@@ -58,6 +58,10 @@
#include <pcre.h>
#ifdef USE_LIBJQ
#include "jq.h"
#endif
static apr_byte_t oidc_authz_match_value(request_rec *r, const char *spec_c,
json_t *val, const char *key) {
......@@ -101,7 +105,7 @@ static apr_byte_t oidc_authz_match_value(request_rec *r, const char *spec_c,
} else if (json_is_boolean(elem)) {
if (apr_strnatcmp(
json_is_true(elem) ? "true" : "false", spec_c) == 0)
json_is_true(elem) ? "true" : "false", spec_c) == 0)
return TRUE;
} else if (json_is_integer(elem)) {
......@@ -180,7 +184,7 @@ static apr_byte_t oidc_authz_match_expression(request_rec *r,
/*
* see if a the Require value matches with a set of provided claims
*/
static apr_byte_t oidc_authz_match_claim(request_rec *r,
apr_byte_t oidc_authz_match_claim(request_rec *r,
const char * const attr_spec, const json_t * const claims) {
const char *key;
......@@ -233,10 +237,73 @@ static apr_byte_t oidc_authz_match_claim(request_rec *r,
return FALSE;
}
#ifdef USE_LIBJQ
static apr_byte_t jq_parse(request_rec *r, jq_state *jq, struct jv_parser *parser) {
apr_byte_t rv = FALSE;
jv value;
while (jv_is_valid((value = jv_parser_next(parser)))) {
jq_start(jq, value, 0);
jv result;
while (jv_is_valid(result = jq_next(jq))) {
jv dumped = jv_dump_string(result, 0);
const char *str = jv_string_value(dumped);
oidc_debug(r, "dumped: %s", str);
rv = (apr_strnatcmp(str, "true") == 0);
}
jv_free(result);
}
if (jv_invalid_has_msg(jv_copy(value))) {
jv msg = jv_invalid_get_msg(value);
oidc_error(r, "invalid: %s", jv_string_value(msg));
jv_free(msg);
rv = FALSE;
} else {
jv_free(value);
}
return rv;
}
/*
* see if a the Require value matches a configured expression
*/
apr_byte_t oidc_authz_match_claims_expr(request_rec *r,
const char * const attr_spec, const json_t * const claims) {
apr_byte_t rv = FALSE;
oidc_debug(r, "enter: '%s'", attr_spec);
jq_state *jq = jq_init();
if (jq_compile(jq, attr_spec) == 0) {
jq_teardown(&jq);
return FALSE;
}
struct jv_parser *parser = jv_parser_new(0);
char *buf = oidc_util_encode_json_object(r, (json_t *)claims, 0);
jv_parser_set_buf(parser, buf, strlen(buf), 0);
rv = jq_parse(r, jq, parser);
jv_parser_free(parser);
jq_teardown(&jq);
return rv;
}
#endif
#if MODULE_MAGIC_NUMBER_MAJOR < 20100714
/*
* Apache <2.4 authorization routine: match the claims from the authenticated user against the Require primitive
*/
int oidc_authz_worker(request_rec *r, const json_t * const claims,
int oidc_authz_worker22(request_rec *r, const json_t * const claims,
const require_line * const reqs, int nelts) {
const int m = r->method_number;
const char *token;
......@@ -244,6 +311,7 @@ int oidc_authz_worker(request_rec *r, const json_t * const claims,
int i;
int have_oauthattr = 0;
int count_oauth_claims = 0;
oidc_authz_match_claim_fn_type match_claim_fn = NULL;
/* go through applicable Require directives */
for (i = 0; i < nelts; ++i) {
......@@ -258,11 +326,18 @@ int oidc_authz_worker(request_rec *r, const json_t * const claims,
token = ap_getword_white(r->pool, &requirement);
if (apr_strnatcasecmp(token, OIDC_REQUIRE_NAME) != 0) {
/* see if we've got anything meant for us */
if (apr_strnatcasecmp(token, OIDC_REQUIRE_CLAIM_NAME) == 0) {
match_claim_fn = oidc_authz_match_claim;
#ifdef USE_LIBJQ
} else if (apr_strnatcasecmp(token, OIDC_REQUIRE_CLAIMS_EXPR_NAME) == 0) {
match_claim_fn = oidc_authz_match_claims_expr;
#endif
} else {
continue;
}
/* ok, we have a "Require claim" to satisfy */
/* ok, we have a "Require claim/claims_expr" to satisfy */
have_oauthattr = 1;
/*
......@@ -276,18 +351,18 @@ int oidc_authz_worker(request_rec *r, const json_t * const claims,
/*
* iterate over the claim specification strings in this require directive searching
* for a specification that matches one of the claims.
* for a specification that matches one of the claims/expressions.
*/
while (*requirement) {
token = ap_getword_conf(r->pool, &requirement);
count_oauth_claims++;
oidc_debug(r, "evaluating claim specification: %s", token);
oidc_debug(r, "evaluating claim/expr specification: %s", token);
if (oidc_authz_match_claim(r, token, claims) == TRUE) {
if (match_claim_fn(r, token, claims) == TRUE) {
/* if *any* claim matches, then authorization has succeeded and all of the others are ignored */
oidc_debug(r, "require claim '%s' matched", token);
oidc_debug(r, "require claim/expr '%s' matched", token);
return OK;
}
}
......@@ -295,13 +370,13 @@ int oidc_authz_worker(request_rec *r, const json_t * const claims,
/* if there weren't any "Require claim" directives, we're irrelevant */
if (!have_oauthattr) {
oidc_debug(r, "no claim statements found, not performing authz");
oidc_debug(r, "no claim/expr statements found, not performing authz");
return DECLINED;
}
/* if there was a "Require claim", but no actual claims, that's cause to warn the admin of an iffy configuration */
if (count_oauth_claims == 0) {
oidc_warn(r,
"'require claim' missing specification(s) in configuration, declining");
"'require claim/expr' missing specification(s) in configuration, declining");
return DECLINED;
}
......@@ -312,20 +387,24 @@ int oidc_authz_worker(request_rec *r, const json_t * const claims,
return HTTP_UNAUTHORIZED;
}
#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
#else
/*
* Apache >=2.4 authorization routine: match the claims from the authenticated user against the Require primitive
*/
authz_status oidc_authz_worker24(request_rec *r, const json_t * const claims, const char *require_args) {
authz_status oidc_authz_worker24(request_rec *r, const json_t * const claims,
const char *require_args, oidc_authz_match_claim_fn_type match_claim_fn) {
int count_oauth_claims = 0;
const char *t, *w;
/* needed for anonymous authentication */
if (r->user == NULL) return AUTHZ_DENIED_NO_USER;
if (r->user == NULL)
return AUTHZ_DENIED_NO_USER;
/* if no claims, impossible to satisfy */
if (!claims) return AUTHZ_DENIED;
if (!claims)
return AUTHZ_DENIED;
/* loop over the Required specifications */
t = require_args;
......@@ -333,12 +412,12 @@ authz_status oidc_authz_worker24(request_rec *r, const json_t * const claims, co
count_oauth_claims++;
oidc_debug(r, "evaluating claim specification: %s", w);
oidc_debug(r, "evaluating claim/expr specification: %s", w);
/* see if we can match any of out input claims against this Require'd value */
if (oidc_authz_match_claim(r, w, claims) == TRUE) {
if (match_claim_fn(r, w, claims) == TRUE) {
oidc_debug(r, "require claim '%s' matched", w);
oidc_debug(r, "require claim/expr '%s' matched", w);
return AUTHZ_GRANTED;
}
}
......@@ -346,9 +425,10 @@ authz_status oidc_authz_worker24(request_rec *r, const json_t * const claims, co
/* if there wasn't anything after the Require claims directive... */
if (count_oauth_claims == 0) {
oidc_warn(r,
"'require claim' missing specification(s) in configuration, denying");
"'require claim/expr' missing specification(s) in configuration, denying");
}
return AUTHZ_DENIED;
}
#endif
/*.lo
/*.o
/*.slo
/.libs
......@@ -66,8 +66,7 @@ typedef apr_byte_t (*oidc_cache_set_function)(request_rec *r,
typedef int (*oidc_cache_destroy_function)(server_rec *s);
typedef struct oidc_cache_t {
apr_byte_t secure;
oidc_cache_cfg_create create_config;
int encrypt_by_default;
oidc_cache_post_config_function post_config;
oidc_cache_child_init_function child_init;
oidc_cache_get_function get;
......@@ -89,6 +88,35 @@ apr_byte_t oidc_cache_mutex_lock(request_rec *r, oidc_cache_mutex_t *m);
apr_byte_t oidc_cache_mutex_unlock(request_rec *r, oidc_cache_mutex_t *m);
apr_byte_t oidc_cache_mutex_destroy(server_rec *s, oidc_cache_mutex_t *m);
apr_byte_t oidc_cache_get(request_rec *r, const char *section, const char *key,
char **value);
apr_byte_t oidc_cache_set(request_rec *r, const char *section, const char *key,
const char *value, apr_time_t expiry);
#define OIDC_CACHE_SECTION_SESSION "s"
#define OIDC_CACHE_SECTION_NONCE "n"
#define OIDC_CACHE_SECTION_JWKS "j"
#define OIDC_CACHE_SECTION_ACCESS_TOKEN "a"
#define OIDC_CACHE_SECTION_PROVIDER "p"
#define OIDC_CACHE_SECTION_JTI "t"
#define OIDC_CACHE_SECTION_REQUEST_URI "r"
#define oidc_cache_get_session(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_SESSION, key, value)
#define oidc_cache_get_nonce(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_NONCE, key, value)
#define oidc_cache_get_jwks(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_JWKS, key, value)
#define oidc_cache_get_access_token(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, key, value)
#define oidc_cache_get_provider(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_PROVIDER, key, value)
#define oidc_cache_get_jti(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_JTI, key, value)
#define oidc_cache_get_request_uri(r, key, value) oidc_cache_get(r, OIDC_CACHE_SECTION_REQUEST_URI, key, value)
#define oidc_cache_set_session(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_SESSION, key, value, expiry)
#define oidc_cache_set_nonce(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_NONCE, key, value, expiry)
#define oidc_cache_set_jwks(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_JWKS, key, value, expiry)
#define oidc_cache_set_access_token(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, key, value, expiry)
#define oidc_cache_set_provider(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_PROVIDER, key, value, expiry)
#define oidc_cache_set_jti(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_JTI, key, value, expiry)
#define oidc_cache_set_request_uri(r, key, value, expiry) oidc_cache_set(r, OIDC_CACHE_SECTION_REQUEST_URI, key, value, expiry)
extern oidc_cache_t oidc_cache_file;
extern oidc_cache_t oidc_cache_memcache;
extern oidc_cache_t oidc_cache_shm;
......
This diff is collapsed.
......@@ -184,7 +184,7 @@ static apr_byte_t oidc_cache_file_get(request_rec *r, const char *section,
/* open the cache file if it exists, otherwise we just have a "regular" cache miss */
if (apr_file_open(&fd, path, APR_FOPEN_READ | APR_FOPEN_BUFFERED,
APR_OS_DEFAULT, r->pool) != APR_SUCCESS) {
APR_OS_DEFAULT, r->pool) != APR_SUCCESS) {
oidc_debug(r, "cache miss for key \"%s\"", key);
return TRUE;
}
......@@ -412,7 +412,8 @@ static apr_byte_t oidc_cache_file_set(request_rec *r, const char *section,
}
/* try to open the cache file for writing, creating it if it does not exist */
if ((rc = apr_file_open(&fd, path, (APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE),
if ((rc = apr_file_open(&fd, path,
(APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE),
APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) {
oidc_error(r, "cache file \"%s\" could not be opened (%s)", path,
apr_strerror(rc, s_err, sizeof(s_err)));
......@@ -435,25 +436,23 @@ static apr_byte_t oidc_cache_file_set(request_rec *r, const char *section,
return FALSE;
/* next write the value */
if ((rc = oidc_cache_file_write(r, path, fd, (void *) value, info.len))
!= APR_SUCCESS)
return FALSE;
rc = oidc_cache_file_write(r, path, fd, (void *) value, info.len);
/* unlock and close the written file */
apr_file_unlock(fd);
apr_file_close(fd);
/* log our success */
/* log our success/failure */
oidc_debug(r,
"set entry for key \"%s\" (%" APR_SIZE_T_FMT " bytes, expires in: %" APR_TIME_T_FMT ")",
key, info.len, apr_time_sec(expiry - apr_time_now()));
"%s stored entry for key \"%s\" (%" APR_SIZE_T_FMT " bytes, expires in: %" APR_TIME_T_FMT ")",
rc ? "successfully" : "could not", key, info.len,
apr_time_sec(expiry - apr_time_now()));
return TRUE;
return rc;
}
oidc_cache_t oidc_cache_file = {
1,
NULL,
oidc_cache_file_post_config,
NULL,
oidc_cache_file_get,
......
......@@ -188,6 +188,17 @@ static char *oidc_cache_memcache_get_key(apr_pool_t *pool, const char *section,
return apr_psprintf(pool, "%s:%s", section, key);
}
/*
* check dead/alive status for all servers
*/
static apr_byte_t oidc_cache_memcache_status(request_rec *r, oidc_cache_cfg_memcache_t *context) {
int rc = TRUE;
int i;
for (i = 0; rc && i < context->cache_memcache->ntotal; i++)
rc = rc && (context->cache_memcache->live_servers[0]->status != APR_MC_SERVER_DEAD);
return rc;
}
/*
* get a name/value pair from memcache
*/
......@@ -209,11 +220,26 @@ static apr_byte_t oidc_cache_memcache_get(request_rec *r, const char *section,
&len, NULL);
if (rv == APR_NOTFOUND) {
/*
* NB: workaround the fact that the apr_memcache returns APR_NOTFOUND if a server has been marked dead
*/
if (oidc_cache_memcache_status(r, context) == FALSE) {
oidc_cache_memcache_log_status_error(r, "apr_memcache_getp", rv);
return FALSE;
}
oidc_debug(r, "apr_memcache_getp: key %s not found in cache",
oidc_cache_memcache_get_key(r->pool, section, key));
return FALSE;
return TRUE;
} else if (rv != APR_SUCCESS) {
oidc_cache_memcache_log_status_error(r, "apr_memcache_getp", rv);
return FALSE;
}
......@@ -252,6 +278,7 @@ static apr_byte_t oidc_cache_memcache_set(request_rec *r, const char *section,
if (rv == APR_NOTFOUND) {
oidc_debug(r, "apr_memcache_delete: key %s not found in cache",
oidc_cache_memcache_get_key(r->pool, section, key));
rv = APR_SUCCESS;
} else if (rv != APR_SUCCESS) {
oidc_cache_memcache_log_status_error(r, "apr_memcache_delete", rv);
}
......@@ -276,7 +303,6 @@ static apr_byte_t oidc_cache_memcache_set(request_rec *r, const char *section,
oidc_cache_t oidc_cache_memcache = {
1,
oidc_cache_memcache_cfg_create,
oidc_cache_memcache_post_config,
NULL,
oidc_cache_memcache_get,
......
......@@ -124,7 +124,8 @@ static int oidc_cache_redis_post_config(server_rec *s) {
context->port = 6379;
if (cfg->cache_redis_password != NULL) {
context->passwd = apr_pstrdup(s->process->pool, cfg->cache_redis_password);
context->passwd = apr_pstrdup(s->process->pool,
cfg->cache_redis_password);
}
if (oidc_cache_mutex_post_config(s, context->mutex, "redis") == FALSE)
......@@ -156,39 +157,64 @@ static char *oidc_cache_redis_get_key(apr_pool_t *pool, const char *section,
/* key for storing data in the process pool */
#define OIDC_CACHE_REDIS_CONTEXT "oidc_cache_redis_context"
/*
* per-process Redis connection context
*/
typedef struct {
redisContext *ctx;
} oidc_cache_redis_ctx_t;
/*
* free resources allocated for the per-process Redis connection context
*/
static apr_status_t oidc_cache_redis_free(void *ptr) {
oidc_cache_redis_ctx_t *rctx = (oidc_cache_redis_ctx_t *) ptr;
if ((rctx != NULL) && (rctx->ctx != NULL)) {
redisFree(rctx->ctx);
rctx->ctx = NULL;
}
return APR_SUCCESS;
}
/*
* connect to Redis server