summaryrefslogtreecommitdiff
path: root/src/reclaim
diff options
context:
space:
mode:
Diffstat (limited to 'src/reclaim')
-rw-r--r--src/reclaim/oidc_helper.c78
-rw-r--r--src/reclaim/oidc_helper.h38
-rw-r--r--src/reclaim/plugin_rest_openid_connect.c151
3 files changed, 217 insertions, 50 deletions
diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c
index c6d56e02d..1dde7b673 100644
--- a/src/reclaim/oidc_helper.c
+++ b/src/reclaim/oidc_helper.c
@@ -567,6 +567,48 @@ OIDC_build_authz_code (const struct GNUNET_IDENTITY_PrivateKey *issuer,
}
+enum GNUNET_GenericReturnValue
+check_code_challenge (const char *code_challenge,
+ uint32_t code_challenge_len,
+ const char *code_verifier)
+{
+ char *code_verifier_hash;
+ char *expected_code_challenge;
+
+ if (0 == code_challenge_len) /* Only check if this code requires a CV */
+ return GNUNET_OK;
+ if (NULL == code_verifier)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected code verifier!\n");
+ return GNUNET_SYSERR;
+ }
+ code_verifier_hash = GNUNET_malloc (256 / 8);
+ // hash code verifier
+ gcry_md_hash_buffer (GCRY_MD_SHA256,
+ code_verifier_hash,
+ code_verifier,
+ strlen (code_verifier));
+ // encode code verifier
+ GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8,
+ &expected_code_challenge);
+ GNUNET_free (code_verifier_hash);
+ if (0 !=
+ strncmp (expected_code_challenge, code_challenge, code_challenge_len))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid code verifier! Expected: %s, Got: %.*s\n",
+ expected_code_challenge,
+ code_challenge_len,
+ code_challenge);
+ GNUNET_free (expected_code_challenge);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (expected_code_challenge);
+ return GNUNET_OK;
+}
+
+
/**
* Parse reclaim ticket and nonce from
* authorization code.
@@ -589,16 +631,15 @@ OIDC_parse_authz_code (const struct GNUNET_IDENTITY_PublicKey *audience,
struct GNUNET_RECLAIM_Ticket *ticket,
struct GNUNET_RECLAIM_AttributeList **attrs,
struct GNUNET_RECLAIM_PresentationList **presentations,
- char **nonce_str)
+ char **nonce_str,
+ enum OIDC_VerificationOptions opts)
{
char *code_payload;
char *ptr;
char *plaintext;
char *attrs_ser;
char *presentations_ser;
- char *expected_code_challenge;
char *code_challenge;
- char *code_verifier_hash;
struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
struct GNUNET_IDENTITY_Signature *signature;
uint32_t code_challenge_len;
@@ -636,38 +677,15 @@ OIDC_parse_authz_code (const struct GNUNET_IDENTITY_PublicKey *audience,
// cmp code_challenge code_verifier
code_challenge_len = ntohl (params->code_challenge_len);
code_challenge = ((char *) &params[1]);
- if (0 != code_challenge_len) /* Only check if this code requires a CV */
+ if (!(opts & OIDC_VERIFICATION_NO_CODE_VERIFIER))
{
- if (NULL == code_verifier)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Expected code verifier!\n");
- GNUNET_free (code_payload);
- return GNUNET_SYSERR;
- }
- code_verifier_hash = GNUNET_malloc (256 / 8);
- // hash code verifier
- gcry_md_hash_buffer (GCRY_MD_SHA256,
- code_verifier_hash,
- code_verifier,
- strlen (code_verifier));
- // encode code verifier
- GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8,
- &expected_code_challenge);
- GNUNET_free (code_verifier_hash);
- if (0 !=
- strncmp (expected_code_challenge, code_challenge, code_challenge_len))
+ if (GNUNET_OK != check_code_challenge (code_challenge,
+ code_challenge_len,
+ code_verifier))
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid code verifier! Expected: %s, Got: %.*s\n",
- expected_code_challenge,
- code_challenge_len,
- code_challenge);
GNUNET_free (code_payload);
- GNUNET_free (expected_code_challenge);
return GNUNET_SYSERR;
}
- GNUNET_free (expected_code_challenge);
}
nonce_len = ntohl (params->nonce_len);
if (0 != nonce_len)
diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h
index eb1022423..2a8b7bbae 100644
--- a/src/reclaim/oidc_helper.h
+++ b/src/reclaim/oidc_helper.h
@@ -38,6 +38,19 @@
#define SERVER_ADDRESS "https://api.reclaim"
+enum OIDC_VerificationOptions
+{
+ /**
+ * Strict verification
+ */
+ OIDC_VERIFICATION_DEFAULT = 0,
+
+ /**
+ * Do not check code verifier even if expected
+ */
+ OIDC_VERIFICATION_NO_CODE_VERIFIER = 1
+};
+
/**
* Create a JWT from attributes
*
@@ -51,12 +64,13 @@
*/
char*
OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key,
- const struct GNUNET_IDENTITY_PublicKey *sub_key,
- const struct GNUNET_RECLAIM_AttributeList *attrs,
- const struct GNUNET_RECLAIM_PresentationList *presentations,
- const struct GNUNET_TIME_Relative *expiration_time,
- const char *nonce,
- const char *secret_key);
+ const struct GNUNET_IDENTITY_PublicKey *sub_key,
+ const struct GNUNET_RECLAIM_AttributeList *attrs,
+ const struct
+ GNUNET_RECLAIM_PresentationList *presentations,
+ const struct GNUNET_TIME_Relative *expiration_time,
+ const char *nonce,
+ const char *secret_key);
/**
* Builds an OIDC authorization code including
@@ -68,13 +82,15 @@ OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key,
* @param presentations credential presentation list
* @param nonce the nonce to include in the code
* @param code_challenge PKCE code challenge
+ * @param opts verification options
* @return a new authorization code (caller must free)
*/
char*
OIDC_build_authz_code (const struct GNUNET_IDENTITY_PrivateKey *issuer,
const struct GNUNET_RECLAIM_Ticket *ticket,
const struct GNUNET_RECLAIM_AttributeList *attrs,
- const struct GNUNET_RECLAIM_PresentationList *presentations,
+ const struct
+ GNUNET_RECLAIM_PresentationList *presentations,
const char *nonce,
const char *code_challenge);
@@ -99,7 +115,8 @@ OIDC_parse_authz_code (const struct GNUNET_IDENTITY_PublicKey *ecdsa_pub,
struct GNUNET_RECLAIM_Ticket *ticket,
struct GNUNET_RECLAIM_AttributeList **attrs,
struct GNUNET_RECLAIM_PresentationList **presentations,
- char **nonce);
+ char **nonce,
+ enum OIDC_VerificationOptions opts);
/**
* Build a token response for a token request
@@ -126,7 +143,7 @@ OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket);
* Parse an access token
*/
int
-OIDC_access_token_parse (const char* token,
+OIDC_access_token_parse (const char*token,
struct GNUNET_RECLAIM_Ticket **ticket);
@@ -154,6 +171,7 @@ OIDC_check_scopes_for_claim_request (const char *scopes,
char *
OIDC_generate_userinfo (const struct GNUNET_IDENTITY_PublicKey *sub_key,
const struct GNUNET_RECLAIM_AttributeList *attrs,
- const struct GNUNET_RECLAIM_PresentationList *presentations);
+ const struct
+ GNUNET_RECLAIM_PresentationList *presentations);
#endif
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c
index 7a8a886bd..8d21a5c99 100644
--- a/src/reclaim/plugin_rest_openid_connect.c
+++ b/src/reclaim/plugin_rest_openid_connect.c
@@ -227,6 +227,11 @@
*/
#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
+/**
+ * How long to wait for a consume in userinfo endpoint
+ */
+#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \
+ GNUNET_TIME_UNIT_SECONDS,2)
/**
* OIDC ignored parameter array
@@ -240,7 +245,12 @@ static char *OIDC_ignored_parameter_array[] = { "display",
"acr_values" };
/**
- * OIDC Hash map that keeps track of issued cookies
+ * OIDC hashmap for cached access tokens and codes
+ */
+struct GNUNET_CONTAINER_MultiHashMap *oidc_code_cache;
+
+/**
+ * OIDC hashmap that keeps track of issued cookies
*/
struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
@@ -460,6 +470,11 @@ struct RequestHandle
struct GNUNET_RECLAIM_Operation *idp_op;
/**
+ * Timeout task for consume
+ */
+ struct GNUNET_SCHEDULER_Task *consume_timeout_op;
+
+ /**
* Attribute iterator
*/
struct GNUNET_RECLAIM_AttributeIterator *attr_it;
@@ -506,6 +521,11 @@ struct RequestHandle
char *url;
/**
+ * The passed access token
+ */
+ char *access_token;
+
+ /**
* The tld for redirect
*/
char *tld;
@@ -571,6 +591,8 @@ cleanup_handle (struct RequestHandle *handle)
GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
if (NULL != handle->idp_op)
GNUNET_RECLAIM_cancel (handle->idp_op);
+ if (NULL != handle->consume_timeout_op)
+ GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
GNUNET_free (handle->url);
GNUNET_free (handle->tld);
GNUNET_free (handle->redirect_prefix);
@@ -601,6 +623,8 @@ cleanup_handle (struct RequestHandle *handle)
GNUNET_CONTAINER_DLL_remove (requests_head,
requests_tail,
handle);
+ if (NULL != handle->access_token)
+ GNUNET_free (handle->access_token);
GNUNET_free (handle);
}
@@ -1282,8 +1306,8 @@ code_redirect (void *cls)
{
if (GNUNET_OK !=
GNUNET_IDENTITY_public_key_from_string (handle->oidc
- ->login_identity,
- &pubkey))
+ ->login_identity,
+ &pubkey))
{
handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
handle->edesc =
@@ -1662,7 +1686,7 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
if (GNUNET_OK !=
GNUNET_IDENTITY_public_key_from_string (handle->oidc->client_id,
- &handle->oidc->client_pkey))
+ &handle->oidc->client_pkey))
{
handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
@@ -2071,7 +2095,8 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
// decode code
if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket,
- &cl, &pl, &nonce))
+ &cl, &pl, &nonce,
+ OIDC_VERIFICATION_DEFAULT))
{
handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
handle->edesc = GNUNET_strdup ("invalid code");
@@ -2080,7 +2105,6 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
GNUNET_SCHEDULER_add_now (&do_error, handle);
return;
}
- GNUNET_free (code);
// create jwt
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
@@ -2091,6 +2115,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
handle->edesc = GNUNET_strdup ("gnunet configuration failed");
handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ GNUNET_free (code);
GNUNET_SCHEDULER_add_now (&do_error, handle);
return;
}
@@ -2105,6 +2130,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
handle->edesc = GNUNET_strdup ("No signing secret configured!");
handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ GNUNET_free (code);
GNUNET_SCHEDULER_add_now (&do_error, handle);
return;
}
@@ -2116,6 +2142,26 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
(NULL != nonce) ? nonce : NULL,
jwt_secret);
access_token = OIDC_access_token_new (&ticket);
+ /* Store mapping from access token to code so we can later
+ * fall back on the provided attributes in userinfo
+ */
+ GNUNET_CRYPTO_hash (access_token,
+ strlen (access_token),
+ &cache_key);
+ char *tmp_at = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
+ &cache_key);
+ GNUNET_CONTAINER_multihashmap_put (oidc_code_cache,
+ &cache_key,
+ code,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
+ /* If there was a previus code in there, free the old value */
+ if (NULL != tmp_at)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "OIDC access token already issued. Cleanup.\n");
+ GNUNET_free (tmp_at);
+ }
+
OIDC_build_token_response (access_token,
id_token,
&expiration_time,
@@ -2149,6 +2195,10 @@ consume_ticket (void *cls,
struct GNUNET_RECLAIM_PresentationListEntry *atle;
struct MHD_Response *resp;
char *result_str;
+
+ if (NULL != handle->consume_timeout_op)
+ GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
+ handle->consume_timeout_op = NULL;
handle->idp_op = NULL;
if (NULL == identity)
@@ -2180,8 +2230,9 @@ consume_ticket (void *cls,
for (atle = handle->presentations->list_head;
NULL != atle; atle = atle->next)
{
- if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&atle->presentation->credential_id,
- &pres->credential_id))
+ if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (
+ &atle->presentation->credential_id,
+ &pres->credential_id))
continue;
break; /** already in list **/
}
@@ -2190,8 +2241,8 @@ consume_ticket (void *cls,
/** Credential matches for attribute, add **/
atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry);
atle->presentation = GNUNET_RECLAIM_presentation_new (pres->type,
- pres->data,
- pres->data_size);
+ pres->data,
+ pres->data_size);
GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head,
handle->presentations->list_tail,
atle);
@@ -2199,6 +2250,69 @@ consume_ticket (void *cls,
}
+static void
+consume_timeout (void*cls)
+{
+ struct RequestHandle *handle = cls;
+ struct GNUNET_HashCode cache_key;
+ struct GNUNET_RECLAIM_AttributeList *cl = NULL;
+ struct GNUNET_RECLAIM_PresentationList *pl = NULL;
+ struct GNUNET_RECLAIM_Ticket ticket;
+ char *nonce;
+ char *cached_code;
+
+ handle->consume_timeout_op = NULL;
+ if (NULL != handle->idp_op)
+ GNUNET_RECLAIM_cancel (handle->idp_op);
+ handle->idp_op = NULL;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Ticket consumptioned timed out. Using cache...\n");
+ GNUNET_CRYPTO_hash (handle->access_token,
+ strlen (handle->access_token),
+ &cache_key);
+ cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
+ &cache_key);
+ if (NULL == cached_code)
+ {
+ handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
+ handle->edesc = GNUNET_strdup ("No Access Token in cache!");
+ handle->response_code = MHD_HTTP_UNAUTHORIZED;
+ GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
+ return;
+ }
+
+ // decode code
+ if (GNUNET_OK != OIDC_parse_authz_code (&handle->ticket.audience,
+ cached_code, NULL, &ticket,
+ &cl, &pl, &nonce,
+ OIDC_VERIFICATION_NO_CODE_VERIFIER))
+ {
+ handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
+ handle->edesc = GNUNET_strdup ("invalid code");
+ handle->response_code = MHD_HTTP_BAD_REQUEST;
+ GNUNET_free (cached_code);
+ GNUNET_SCHEDULER_add_now (&do_error, handle);
+ return;
+ }
+
+ struct MHD_Response *resp;
+ char *result_str;
+
+ result_str = OIDC_generate_userinfo (&handle->ticket.identity,
+ cl,
+ pl);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
+ resp = GNUNET_REST_create_response (result_str);
+ handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
+ GNUNET_free (result_str);
+ GNUNET_free (nonce);
+ GNUNET_RECLAIM_attribute_list_destroy (cl);
+ GNUNET_RECLAIM_presentation_list_destroy (pl);
+ cleanup_handle (handle);
+}
+
+
/**
* Responds to userinfo GET and url-encoded POST request
*
@@ -2295,6 +2409,11 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
handle->presentations =
GNUNET_new (struct GNUNET_RECLAIM_PresentationList);
+ /* If the consume takes too long, we use values from the cache */
+ handle->access_token = GNUNET_strdup (authorization_access_token);
+ handle->consume_timeout_op = GNUNET_SCHEDULER_add_delayed (CONSUME_TIMEOUT,
+ &consume_timeout,
+ handle);
handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
privkey,
&handle->ticket,
@@ -2554,6 +2673,10 @@ rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
if (NULL == OIDC_cookie_jar_map)
OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10,
GNUNET_NO);
+ if (NULL == oidc_code_cache)
+ oidc_code_cache = GNUNET_CONTAINER_multihashmap_create (10,
+ GNUNET_NO);
+
handle->response_code = 0;
handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
handle->proc_cls = proc_cls;
@@ -2646,6 +2769,14 @@ libgnunet_plugin_rest_openid_connect_done (void *cls)
NULL);
GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
}
+ if (NULL != oidc_code_cache)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (oidc_code_cache,
+ &cleanup_hashmap,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (oidc_code_cache);
+ }
+
GNUNET_free (allow_methods);
if (NULL != gns_handle)
GNUNET_GNS_disconnect (gns_handle);