mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-09 16:26:59 +08:00
Attempt to download updated JWKs if the client presents an unknown key.
Limit the download of keys to every 900s. Resolves: #284 Signed-off-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
@@ -35,9 +35,14 @@
|
||||
#include <cjose/cjose.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MINIMUM_KEY_REFRESH_INTERVAL (900)
|
||||
|
||||
typedef struct oidc_vctx_st {
|
||||
json_t *config;
|
||||
json_t *jwks;
|
||||
void * pool;
|
||||
int minimum_jwk_refresh_time;
|
||||
time_t last_jwks_load_time;
|
||||
} oidc_vctx_st;
|
||||
|
||||
typedef struct oidc_ctx_st {
|
||||
@@ -46,7 +51,7 @@ typedef struct oidc_ctx_st {
|
||||
int token_verified;
|
||||
} oidc_ctx_st;
|
||||
|
||||
static bool oidc_fetch_oidc_keys(void * pool, oidc_vctx_st * vctx);
|
||||
static bool oidc_fetch_oidc_keys(oidc_vctx_st * vctx);
|
||||
static bool oidc_verify_token(oidc_vctx_st * vctx, const char *token,
|
||||
size_t token_length,
|
||||
char user_name[MAX_USERNAME_SIZE]);
|
||||
@@ -64,6 +69,7 @@ static void oidc_vhost_init(void **vctx, void *pool, void *additional)
|
||||
}
|
||||
vc->config = NULL;
|
||||
vc->jwks = NULL;
|
||||
vc->pool = pool;
|
||||
|
||||
if (config == NULL) {
|
||||
syslog(LOG_ERR, "ocserv-oidc: no configuration passed!\n");
|
||||
@@ -94,7 +100,13 @@ static void oidc_vhost_init(void **vctx, void *pool, void *additional)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!oidc_fetch_oidc_keys(pool, vc)) {
|
||||
if (json_object_get(vc->config, "minimum_jwk_refresh_time")) {
|
||||
vc->minimum_jwk_refresh_time = json_integer_value(json_object_get(vc->config, "minimum_jwk_refresh_time"));
|
||||
} else {
|
||||
vc->minimum_jwk_refresh_time = MINIMUM_KEY_REFRESH_INTERVAL;
|
||||
}
|
||||
|
||||
if (!oidc_fetch_oidc_keys(vc)) {
|
||||
syslog(LOG_ERR, "ocserv-oidc: failed to load jwks\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -303,13 +315,17 @@ static json_t *oidc_fetch_json_from_uri(void * pool, const char *uri)
|
||||
}
|
||||
|
||||
// Download and parse the JWT keys for this virtual server context
|
||||
static bool oidc_fetch_oidc_keys(void * pool, oidc_vctx_st * vctx)
|
||||
static bool oidc_fetch_oidc_keys(oidc_vctx_st * vctx)
|
||||
{
|
||||
bool result = false;
|
||||
json_t *jwks = NULL;
|
||||
json_t *openid_configuration_url =
|
||||
json_object_get(vctx->config, "openid_configuration_url");
|
||||
|
||||
json_t *array;
|
||||
size_t index;
|
||||
json_t *value;
|
||||
|
||||
if (!openid_configuration_url) {
|
||||
syslog(LOG_AUTH,
|
||||
"ocserv-oidc: openid_configuration_url missing from config\n");
|
||||
@@ -317,7 +333,7 @@ static bool oidc_fetch_oidc_keys(void * pool, oidc_vctx_st * vctx)
|
||||
}
|
||||
|
||||
json_t *oidc_config =
|
||||
oidc_fetch_json_from_uri(pool,
|
||||
oidc_fetch_json_from_uri(vctx->pool,
|
||||
json_string_value
|
||||
(openid_configuration_url));
|
||||
|
||||
@@ -334,7 +350,7 @@ static bool oidc_fetch_oidc_keys(void * pool, oidc_vctx_st * vctx)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
jwks = oidc_fetch_json_from_uri(pool, json_string_value(jwks_uri));
|
||||
jwks = oidc_fetch_json_from_uri(vctx->pool, json_string_value(jwks_uri));
|
||||
if (!jwks) {
|
||||
syslog(LOG_AUTH,
|
||||
"ocserv-oidc: failed to fetch keys from jwks_uri %s\n",
|
||||
@@ -342,10 +358,27 @@ static bool oidc_fetch_oidc_keys(void * pool, oidc_vctx_st * vctx)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
array = json_object_get(jwks, "keys");
|
||||
if (array == NULL) {
|
||||
syslog(LOG_AUTH, "ocserv-oidc: JWK keys malformed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Log the keys obtained
|
||||
json_array_foreach(array, index, value) {
|
||||
json_t *key_kid = json_object_get(value, "kid");
|
||||
syslog(LOG_INFO,
|
||||
"ocserv-oidc: fetched new JWK %s\n",
|
||||
json_string_value(key_kid)
|
||||
);
|
||||
}
|
||||
|
||||
if (vctx->jwks) {
|
||||
json_decref(vctx->jwks);
|
||||
}
|
||||
|
||||
vctx->last_jwks_load_time = time(0);
|
||||
|
||||
vctx->jwks = jwks;
|
||||
jwks = NULL;
|
||||
result = true;
|
||||
@@ -537,8 +570,20 @@ static bool oidc_verify_singature(oidc_vctx_st * vctx, cjose_jws_t * jws)
|
||||
}
|
||||
|
||||
if (jwk == NULL) {
|
||||
time_t now;
|
||||
syslog(LOG_AUTH, "ocserv-oidc: JWK with kid=%s not found\n",
|
||||
json_string_value(token_kid));
|
||||
|
||||
syslog(LOG_AUTH, "ocserv-oidc: attempting to download new JWKs");
|
||||
now = time(0);
|
||||
if ((now - vctx->last_jwks_load_time) > vctx->minimum_jwk_refresh_time) {
|
||||
oidc_fetch_oidc_keys(vctx);
|
||||
}
|
||||
else {
|
||||
syslog(LOG_AUTH, "ocserv-oidc: skipping JWK refresh");
|
||||
}
|
||||
|
||||
// Fail the request and let the client try again.
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ json_t *create_oidc_config(const char *openid_configuration_url,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (json_object_set_new(config, "minimum_jwk_refresh_time", json_integer(0))) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
required_claims = NULL;
|
||||
|
||||
result = true;
|
||||
@@ -257,19 +261,21 @@ int main(int argc, char **argv)
|
||||
const char audience[] = "SomeAudience";
|
||||
const char issuer[] = "SomeIssuer";
|
||||
const char user_name_claim[] = "preferred_user_name";
|
||||
const char kid[] = "My Fake Key";
|
||||
char kid[64];
|
||||
const char user_name[] = "SomeUser";
|
||||
const char typ[] = "JWT";
|
||||
const char alg[] = "ES256";
|
||||
time_t now = time(NULL);
|
||||
|
||||
snprintf(kid, sizeof(kid), "key_%ld", now);
|
||||
|
||||
if (!getcwd(working_directory, sizeof(working_directory))) {
|
||||
return 1;
|
||||
}
|
||||
strncat(working_directory, "/data", sizeof(working_directory)-1);
|
||||
working_directory[sizeof(working_directory)-1] = 0;
|
||||
|
||||
cjose_jwk_t *key = create_key("My Fake Key");
|
||||
cjose_jwk_t *key = create_key(kid);
|
||||
|
||||
generate_config_files(working_directory, key, audience, issuer,
|
||||
user_name_claim);
|
||||
|
||||
@@ -46,6 +46,26 @@ for token in data/fail_*; do
|
||||
fi
|
||||
done
|
||||
|
||||
sleep 5s
|
||||
|
||||
# Generate new OIDC keys
|
||||
# First client should fail, triggering reload of keys
|
||||
`dirname $0`/gen_oidc_test_data
|
||||
for token in data/success_*; do
|
||||
http_result=$(LD_PRELOAD=libsocket_wrapper.so curl --insecure https://$ADDRESS:$PORT --request POST --data config-auth.xml --header "Authorization:Bearer=`cat $token`" --output /dev/null --write-out "%{http_code}")
|
||||
if [ "$http_result" != "401" ]; then
|
||||
fail $PID "Token incorrectly accepted returned $http_result"
|
||||
fi
|
||||
done
|
||||
|
||||
# Second client should succeed with new keys
|
||||
for token in data/success_*; do
|
||||
http_result=$(LD_PRELOAD=libsocket_wrapper.so curl --insecure https://$ADDRESS:$PORT --request POST --data config-auth.xml --header "Authorization:Bearer=`cat $token`" --output /dev/null --write-out "%{http_code}")
|
||||
if [ "$http_result" != "200" ]; then
|
||||
fail $PID "Token incorrectly rejected returned $http_result"
|
||||
fi
|
||||
done
|
||||
|
||||
if ! test -f ${PIDFILE};then
|
||||
fail $PID "Could not find pid file ${PIDFILE}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user