diff options
Diffstat (limited to 'src/reclaim/plugin_rest_openid_connect.c')
-rw-r--r-- | src/reclaim/plugin_rest_openid_connect.c | 151 |
1 files changed, 141 insertions, 10 deletions
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 @@ | |||
227 | */ | 227 | */ |
228 | #define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied" | 228 | #define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied" |
229 | 229 | ||
230 | /** | ||
231 | * How long to wait for a consume in userinfo endpoint | ||
232 | */ | ||
233 | #define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \ | ||
234 | GNUNET_TIME_UNIT_SECONDS,2) | ||
230 | 235 | ||
231 | /** | 236 | /** |
232 | * OIDC ignored parameter array | 237 | * OIDC ignored parameter array |
@@ -240,7 +245,12 @@ static char *OIDC_ignored_parameter_array[] = { "display", | |||
240 | "acr_values" }; | 245 | "acr_values" }; |
241 | 246 | ||
242 | /** | 247 | /** |
243 | * OIDC Hash map that keeps track of issued cookies | 248 | * OIDC hashmap for cached access tokens and codes |
249 | */ | ||
250 | struct GNUNET_CONTAINER_MultiHashMap *oidc_code_cache; | ||
251 | |||
252 | /** | ||
253 | * OIDC hashmap that keeps track of issued cookies | ||
244 | */ | 254 | */ |
245 | struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map; | 255 | struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map; |
246 | 256 | ||
@@ -460,6 +470,11 @@ struct RequestHandle | |||
460 | struct GNUNET_RECLAIM_Operation *idp_op; | 470 | struct GNUNET_RECLAIM_Operation *idp_op; |
461 | 471 | ||
462 | /** | 472 | /** |
473 | * Timeout task for consume | ||
474 | */ | ||
475 | struct GNUNET_SCHEDULER_Task *consume_timeout_op; | ||
476 | |||
477 | /** | ||
463 | * Attribute iterator | 478 | * Attribute iterator |
464 | */ | 479 | */ |
465 | struct GNUNET_RECLAIM_AttributeIterator *attr_it; | 480 | struct GNUNET_RECLAIM_AttributeIterator *attr_it; |
@@ -506,6 +521,11 @@ struct RequestHandle | |||
506 | char *url; | 521 | char *url; |
507 | 522 | ||
508 | /** | 523 | /** |
524 | * The passed access token | ||
525 | */ | ||
526 | char *access_token; | ||
527 | |||
528 | /** | ||
509 | * The tld for redirect | 529 | * The tld for redirect |
510 | */ | 530 | */ |
511 | char *tld; | 531 | char *tld; |
@@ -571,6 +591,8 @@ cleanup_handle (struct RequestHandle *handle) | |||
571 | GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); | 591 | GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); |
572 | if (NULL != handle->idp_op) | 592 | if (NULL != handle->idp_op) |
573 | GNUNET_RECLAIM_cancel (handle->idp_op); | 593 | GNUNET_RECLAIM_cancel (handle->idp_op); |
594 | if (NULL != handle->consume_timeout_op) | ||
595 | GNUNET_SCHEDULER_cancel (handle->consume_timeout_op); | ||
574 | GNUNET_free (handle->url); | 596 | GNUNET_free (handle->url); |
575 | GNUNET_free (handle->tld); | 597 | GNUNET_free (handle->tld); |
576 | GNUNET_free (handle->redirect_prefix); | 598 | GNUNET_free (handle->redirect_prefix); |
@@ -601,6 +623,8 @@ cleanup_handle (struct RequestHandle *handle) | |||
601 | GNUNET_CONTAINER_DLL_remove (requests_head, | 623 | GNUNET_CONTAINER_DLL_remove (requests_head, |
602 | requests_tail, | 624 | requests_tail, |
603 | handle); | 625 | handle); |
626 | if (NULL != handle->access_token) | ||
627 | GNUNET_free (handle->access_token); | ||
604 | GNUNET_free (handle); | 628 | GNUNET_free (handle); |
605 | } | 629 | } |
606 | 630 | ||
@@ -1282,8 +1306,8 @@ code_redirect (void *cls) | |||
1282 | { | 1306 | { |
1283 | if (GNUNET_OK != | 1307 | if (GNUNET_OK != |
1284 | GNUNET_IDENTITY_public_key_from_string (handle->oidc | 1308 | GNUNET_IDENTITY_public_key_from_string (handle->oidc |
1285 | ->login_identity, | 1309 | ->login_identity, |
1286 | &pubkey)) | 1310 | &pubkey)) |
1287 | { | 1311 | { |
1288 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE); | 1312 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE); |
1289 | handle->edesc = | 1313 | handle->edesc = |
@@ -1662,7 +1686,7 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
1662 | 1686 | ||
1663 | if (GNUNET_OK != | 1687 | if (GNUNET_OK != |
1664 | GNUNET_IDENTITY_public_key_from_string (handle->oidc->client_id, | 1688 | GNUNET_IDENTITY_public_key_from_string (handle->oidc->client_id, |
1665 | &handle->oidc->client_pkey)) | 1689 | &handle->oidc->client_pkey)) |
1666 | { | 1690 | { |
1667 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT); | 1691 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT); |
1668 | handle->edesc = GNUNET_strdup ("The client is not authorized to request an " | 1692 | handle->edesc = GNUNET_strdup ("The client is not authorized to request an " |
@@ -2071,7 +2095,8 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
2071 | 2095 | ||
2072 | // decode code | 2096 | // decode code |
2073 | if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket, | 2097 | if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket, |
2074 | &cl, &pl, &nonce)) | 2098 | &cl, &pl, &nonce, |
2099 | OIDC_VERIFICATION_DEFAULT)) | ||
2075 | { | 2100 | { |
2076 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); | 2101 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); |
2077 | handle->edesc = GNUNET_strdup ("invalid code"); | 2102 | handle->edesc = GNUNET_strdup ("invalid code"); |
@@ -2080,7 +2105,6 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
2080 | GNUNET_SCHEDULER_add_now (&do_error, handle); | 2105 | GNUNET_SCHEDULER_add_now (&do_error, handle); |
2081 | return; | 2106 | return; |
2082 | } | 2107 | } |
2083 | GNUNET_free (code); | ||
2084 | 2108 | ||
2085 | // create jwt | 2109 | // create jwt |
2086 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, | 2110 | if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, |
@@ -2091,6 +2115,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
2091 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); | 2115 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR); |
2092 | handle->edesc = GNUNET_strdup ("gnunet configuration failed"); | 2116 | handle->edesc = GNUNET_strdup ("gnunet configuration failed"); |
2093 | handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; | 2117 | handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; |
2118 | GNUNET_free (code); | ||
2094 | GNUNET_SCHEDULER_add_now (&do_error, handle); | 2119 | GNUNET_SCHEDULER_add_now (&do_error, handle); |
2095 | return; | 2120 | return; |
2096 | } | 2121 | } |
@@ -2105,6 +2130,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
2105 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); | 2130 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); |
2106 | handle->edesc = GNUNET_strdup ("No signing secret configured!"); | 2131 | handle->edesc = GNUNET_strdup ("No signing secret configured!"); |
2107 | handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; | 2132 | handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; |
2133 | GNUNET_free (code); | ||
2108 | GNUNET_SCHEDULER_add_now (&do_error, handle); | 2134 | GNUNET_SCHEDULER_add_now (&do_error, handle); |
2109 | return; | 2135 | return; |
2110 | } | 2136 | } |
@@ -2116,6 +2142,26 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
2116 | (NULL != nonce) ? nonce : NULL, | 2142 | (NULL != nonce) ? nonce : NULL, |
2117 | jwt_secret); | 2143 | jwt_secret); |
2118 | access_token = OIDC_access_token_new (&ticket); | 2144 | access_token = OIDC_access_token_new (&ticket); |
2145 | /* Store mapping from access token to code so we can later | ||
2146 | * fall back on the provided attributes in userinfo | ||
2147 | */ | ||
2148 | GNUNET_CRYPTO_hash (access_token, | ||
2149 | strlen (access_token), | ||
2150 | &cache_key); | ||
2151 | char *tmp_at = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache, | ||
2152 | &cache_key); | ||
2153 | GNUNET_CONTAINER_multihashmap_put (oidc_code_cache, | ||
2154 | &cache_key, | ||
2155 | code, | ||
2156 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); | ||
2157 | /* If there was a previus code in there, free the old value */ | ||
2158 | if (NULL != tmp_at) | ||
2159 | { | ||
2160 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
2161 | "OIDC access token already issued. Cleanup.\n"); | ||
2162 | GNUNET_free (tmp_at); | ||
2163 | } | ||
2164 | |||
2119 | OIDC_build_token_response (access_token, | 2165 | OIDC_build_token_response (access_token, |
2120 | id_token, | 2166 | id_token, |
2121 | &expiration_time, | 2167 | &expiration_time, |
@@ -2149,6 +2195,10 @@ consume_ticket (void *cls, | |||
2149 | struct GNUNET_RECLAIM_PresentationListEntry *atle; | 2195 | struct GNUNET_RECLAIM_PresentationListEntry *atle; |
2150 | struct MHD_Response *resp; | 2196 | struct MHD_Response *resp; |
2151 | char *result_str; | 2197 | char *result_str; |
2198 | |||
2199 | if (NULL != handle->consume_timeout_op) | ||
2200 | GNUNET_SCHEDULER_cancel (handle->consume_timeout_op); | ||
2201 | handle->consume_timeout_op = NULL; | ||
2152 | handle->idp_op = NULL; | 2202 | handle->idp_op = NULL; |
2153 | 2203 | ||
2154 | if (NULL == identity) | 2204 | if (NULL == identity) |
@@ -2180,8 +2230,9 @@ consume_ticket (void *cls, | |||
2180 | for (atle = handle->presentations->list_head; | 2230 | for (atle = handle->presentations->list_head; |
2181 | NULL != atle; atle = atle->next) | 2231 | NULL != atle; atle = atle->next) |
2182 | { | 2232 | { |
2183 | if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&atle->presentation->credential_id, | 2233 | if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal ( |
2184 | &pres->credential_id)) | 2234 | &atle->presentation->credential_id, |
2235 | &pres->credential_id)) | ||
2185 | continue; | 2236 | continue; |
2186 | break; /** already in list **/ | 2237 | break; /** already in list **/ |
2187 | } | 2238 | } |
@@ -2190,8 +2241,8 @@ consume_ticket (void *cls, | |||
2190 | /** Credential matches for attribute, add **/ | 2241 | /** Credential matches for attribute, add **/ |
2191 | atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); | 2242 | atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); |
2192 | atle->presentation = GNUNET_RECLAIM_presentation_new (pres->type, | 2243 | atle->presentation = GNUNET_RECLAIM_presentation_new (pres->type, |
2193 | pres->data, | 2244 | pres->data, |
2194 | pres->data_size); | 2245 | pres->data_size); |
2195 | GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head, | 2246 | GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head, |
2196 | handle->presentations->list_tail, | 2247 | handle->presentations->list_tail, |
2197 | atle); | 2248 | atle); |
@@ -2199,6 +2250,69 @@ consume_ticket (void *cls, | |||
2199 | } | 2250 | } |
2200 | 2251 | ||
2201 | 2252 | ||
2253 | static void | ||
2254 | consume_timeout (void*cls) | ||
2255 | { | ||
2256 | struct RequestHandle *handle = cls; | ||
2257 | struct GNUNET_HashCode cache_key; | ||
2258 | struct GNUNET_RECLAIM_AttributeList *cl = NULL; | ||
2259 | struct GNUNET_RECLAIM_PresentationList *pl = NULL; | ||
2260 | struct GNUNET_RECLAIM_Ticket ticket; | ||
2261 | char *nonce; | ||
2262 | char *cached_code; | ||
2263 | |||
2264 | handle->consume_timeout_op = NULL; | ||
2265 | if (NULL != handle->idp_op) | ||
2266 | GNUNET_RECLAIM_cancel (handle->idp_op); | ||
2267 | handle->idp_op = NULL; | ||
2268 | |||
2269 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
2270 | "Ticket consumptioned timed out. Using cache...\n"); | ||
2271 | GNUNET_CRYPTO_hash (handle->access_token, | ||
2272 | strlen (handle->access_token), | ||
2273 | &cache_key); | ||
2274 | cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache, | ||
2275 | &cache_key); | ||
2276 | if (NULL == cached_code) | ||
2277 | { | ||
2278 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN); | ||
2279 | handle->edesc = GNUNET_strdup ("No Access Token in cache!"); | ||
2280 | handle->response_code = MHD_HTTP_UNAUTHORIZED; | ||
2281 | GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle); | ||
2282 | return; | ||
2283 | } | ||
2284 | |||
2285 | // decode code | ||
2286 | if (GNUNET_OK != OIDC_parse_authz_code (&handle->ticket.audience, | ||
2287 | cached_code, NULL, &ticket, | ||
2288 | &cl, &pl, &nonce, | ||
2289 | OIDC_VERIFICATION_NO_CODE_VERIFIER)) | ||
2290 | { | ||
2291 | handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); | ||
2292 | handle->edesc = GNUNET_strdup ("invalid code"); | ||
2293 | handle->response_code = MHD_HTTP_BAD_REQUEST; | ||
2294 | GNUNET_free (cached_code); | ||
2295 | GNUNET_SCHEDULER_add_now (&do_error, handle); | ||
2296 | return; | ||
2297 | } | ||
2298 | |||
2299 | struct MHD_Response *resp; | ||
2300 | char *result_str; | ||
2301 | |||
2302 | result_str = OIDC_generate_userinfo (&handle->ticket.identity, | ||
2303 | cl, | ||
2304 | pl); | ||
2305 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str); | ||
2306 | resp = GNUNET_REST_create_response (result_str); | ||
2307 | handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); | ||
2308 | GNUNET_free (result_str); | ||
2309 | GNUNET_free (nonce); | ||
2310 | GNUNET_RECLAIM_attribute_list_destroy (cl); | ||
2311 | GNUNET_RECLAIM_presentation_list_destroy (pl); | ||
2312 | cleanup_handle (handle); | ||
2313 | } | ||
2314 | |||
2315 | |||
2202 | /** | 2316 | /** |
2203 | * Responds to userinfo GET and url-encoded POST request | 2317 | * Responds to userinfo GET and url-encoded POST request |
2204 | * | 2318 | * |
@@ -2295,6 +2409,11 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle, | |||
2295 | handle->presentations = | 2409 | handle->presentations = |
2296 | GNUNET_new (struct GNUNET_RECLAIM_PresentationList); | 2410 | GNUNET_new (struct GNUNET_RECLAIM_PresentationList); |
2297 | 2411 | ||
2412 | /* If the consume takes too long, we use values from the cache */ | ||
2413 | handle->access_token = GNUNET_strdup (authorization_access_token); | ||
2414 | handle->consume_timeout_op = GNUNET_SCHEDULER_add_delayed (CONSUME_TIMEOUT, | ||
2415 | &consume_timeout, | ||
2416 | handle); | ||
2298 | handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp, | 2417 | handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp, |
2299 | privkey, | 2418 | privkey, |
2300 | &handle->ticket, | 2419 | &handle->ticket, |
@@ -2554,6 +2673,10 @@ rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle, | |||
2554 | if (NULL == OIDC_cookie_jar_map) | 2673 | if (NULL == OIDC_cookie_jar_map) |
2555 | OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, | 2674 | OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, |
2556 | GNUNET_NO); | 2675 | GNUNET_NO); |
2676 | if (NULL == oidc_code_cache) | ||
2677 | oidc_code_cache = GNUNET_CONTAINER_multihashmap_create (10, | ||
2678 | GNUNET_NO); | ||
2679 | |||
2557 | handle->response_code = 0; | 2680 | handle->response_code = 0; |
2558 | handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; | 2681 | handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; |
2559 | handle->proc_cls = proc_cls; | 2682 | handle->proc_cls = proc_cls; |
@@ -2646,6 +2769,14 @@ libgnunet_plugin_rest_openid_connect_done (void *cls) | |||
2646 | NULL); | 2769 | NULL); |
2647 | GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map); | 2770 | GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map); |
2648 | } | 2771 | } |
2772 | if (NULL != oidc_code_cache) | ||
2773 | { | ||
2774 | GNUNET_CONTAINER_multihashmap_iterate (oidc_code_cache, | ||
2775 | &cleanup_hashmap, | ||
2776 | NULL); | ||
2777 | GNUNET_CONTAINER_multihashmap_destroy (oidc_code_cache); | ||
2778 | } | ||
2779 | |||
2649 | GNUNET_free (allow_methods); | 2780 | GNUNET_free (allow_methods); |
2650 | if (NULL != gns_handle) | 2781 | if (NULL != gns_handle) |
2651 | GNUNET_GNS_disconnect (gns_handle); | 2782 | GNUNET_GNS_disconnect (gns_handle); |