gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet] branch master updated: RECLAIM: Return userinfo claims from cac


From: gnunet
Subject: [gnunet] branch master updated: RECLAIM: Return userinfo claims from cache
Date: Tue, 08 Dec 2020 08:50:58 +0100

This is an automated email from the git hooks/post-receive script.

martin-schanzenbach pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new 7ce054864 RECLAIM: Return userinfo claims from cache
7ce054864 is described below

commit 7ce054864a112f459a75ab542f844a8be0e6c47c
Author: Martin Schanzenbach <schanzen@gnunet.org>
AuthorDate: Tue Dec 8 16:50:27 2020 +0900

    RECLAIM: Return userinfo claims from cache
---
 src/reclaim/oidc_helper.c                |  78 ++++++++++------
 src/reclaim/oidc_helper.h                |  38 ++++++--
 src/reclaim/plugin_rest_openid_connect.c | 151 +++++++++++++++++++++++++++++--
 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;
 
@@ -459,6 +469,11 @@ struct RequestHandle
    */
   struct GNUNET_RECLAIM_Operation *idp_op;
 
+  /**
+   * Timeout task for consume
+   */
+  struct GNUNET_SCHEDULER_Task *consume_timeout_op;
+
   /**
    * Attribute iterator
    */
@@ -505,6 +520,11 @@ struct RequestHandle
    */
   char *url;
 
+  /**
+   * The passed access token
+   */
+  char *access_token;
+
   /**
    * The tld for redirect
    */
@@ -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);

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]