aboutsummaryrefslogtreecommitdiff
path: root/src/reclaim/plugin_rest_openid_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reclaim/plugin_rest_openid_connect.c')
-rw-r--r--src/reclaim/plugin_rest_openid_connect.c2860
1 files changed, 0 insertions, 2860 deletions
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c
deleted file mode 100644
index 1c00abcbd..000000000
--- a/src/reclaim/plugin_rest_openid_connect.c
+++ /dev/null
@@ -1,2860 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file identity/plugin_rest_openid_connect.c
24 * @brief GNUnet Namestore REST plugin
25 *
26 */
27#include "platform.h"
28#include <inttypes.h>
29#include <jansson.h>
30
31#include "gnunet_buffer_lib.h"
32#include "gnunet_strings_lib.h"
33#include "gnunet_gns_service.h"
34#include "gnunet_gnsrecord_lib.h"
35#include "gnunet_identity_service.h"
36#include "gnunet_namestore_service.h"
37#include "gnunet_reclaim_lib.h"
38#include "gnunet_reclaim_service.h"
39#include "gnunet_rest_lib.h"
40#include "gnunet_rest_plugin.h"
41#include "gnunet_signatures.h"
42#include "microhttpd.h"
43#include "oidc_helper.h"
44
45/**
46 * REST root namespace
47 */
48#define GNUNET_REST_API_NS_OIDC "/openid"
49
50/**
51 * OIDC config
52 */
53#define GNUNET_REST_API_NS_OIDC_CONFIG "/.well-known/openid-configuration"
54
55/**
56 * Authorize endpoint
57 */
58#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
59
60/**
61 * Token endpoint
62 */
63#define GNUNET_REST_API_NS_TOKEN "/openid/token"
64
65/**
66 * UserInfo endpoint
67 */
68#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
69
70/**
71 * Login namespace
72 */
73#define GNUNET_REST_API_NS_LOGIN "/openid/login"
74
75/**
76 * State while collecting all egos
77 */
78#define ID_REST_STATE_INIT 0
79
80/**
81 * Done collecting egos
82 */
83#define ID_REST_STATE_POST_INIT 1
84
85/**
86 * OIDC grant_type key
87 */
88#define OIDC_GRANT_TYPE_KEY "grant_type"
89
90/**
91 * OIDC grant_type key
92 */
93#define OIDC_GRANT_TYPE_VALUE "authorization_code"
94
95/**
96 * OIDC code key
97 */
98#define OIDC_CODE_KEY "code"
99
100/**
101 * OIDC response_type key
102 */
103#define OIDC_RESPONSE_TYPE_KEY "response_type"
104
105/**
106 * OIDC client_id key
107 */
108#define OIDC_CLIENT_ID_KEY "client_id"
109
110/**
111 * OIDC scope key
112 */
113#define OIDC_SCOPE_KEY "scope"
114
115/**
116 * OIDC redirect_uri key
117 */
118#define OIDC_REDIRECT_URI_KEY "redirect_uri"
119
120/**
121 * OIDC state key
122 */
123#define OIDC_STATE_KEY "state"
124
125/**
126 * OIDC nonce key
127 */
128#define OIDC_NONCE_KEY "nonce"
129
130/**
131 * OIDC claims key
132 */
133#define OIDC_CLAIMS_KEY "claims"
134
135/**
136 * OIDC PKCE code challenge
137 */
138#define OIDC_CODE_CHALLENGE_KEY "code_challenge"
139
140/**
141 * OIDC PKCE code verifier
142 */
143#define OIDC_CODE_VERIFIER_KEY "code_verifier"
144
145/**
146 * OIDC cookie expiration (in seconds)
147 */
148#define OIDC_COOKIE_EXPIRATION 3
149
150/**
151 * OIDC cookie header key
152 */
153#define OIDC_COOKIE_HEADER_KEY "cookie"
154
155/**
156 * OIDC cookie header information key
157 */
158#define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
159
160/**
161 * OIDC cookie header information key
162 */
163#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
164
165/**
166 * OIDC cookie header if user cancelled
167 */
168#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
169
170/**
171 * OIDC expected response_type while authorizing
172 */
173#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
174
175/**
176 * OIDC expected scope part while authorizing
177 */
178#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
179
180/**
181 * OIDC error key for invalid client
182 */
183#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
184
185/**
186 * OIDC error key for invalid scopes
187 */
188#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
189
190/**
191 * OIDC error key for invalid requests
192 */
193#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
194
195/**
196 * OIDC error key for invalid tokens
197 */
198#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
199
200/**
201 * OIDC error key for invalid cookies
202 */
203#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
204
205/**
206 * OIDC error key for generic server errors
207 */
208#define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
209
210/**
211 * OIDC error key for unsupported grants
212 */
213#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
214
215/**
216 * OIDC error key for unsupported response types
217 */
218#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
219
220/**
221 * OIDC error key for unauthorized clients
222 */
223#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
224
225/**
226 * OIDC error key for denied access
227 */
228#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
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)
235
236/**
237 * OIDC ignored parameter array
238 */
239static char *OIDC_ignored_parameter_array[] = { "display",
240 "prompt",
241 "ui_locales",
242 "response_mode",
243 "id_token_hint",
244 "login_hint",
245 "acr_values" };
246
247/**
248 * OIDC hashmap for cached access tokens and codes
249 */
250struct GNUNET_CONTAINER_MultiHashMap *oidc_code_cache;
251
252/**
253 * OIDC hashmap that keeps track of issued cookies
254 */
255struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
256
257/**
258 * The configuration handle
259 */
260const struct GNUNET_CONFIGURATION_Handle *cfg;
261
262/**
263 * HTTP methods allows for this plugin
264 */
265static char *allow_methods;
266
267/**
268 * Ego list
269 */
270static struct EgoEntry *ego_head;
271
272/**
273 * Ego list
274 */
275static struct EgoEntry *ego_tail;
276
277/**
278 * The processing state
279 */
280static int state;
281
282/**
283 * Handle to Identity service.
284 */
285static struct GNUNET_IDENTITY_Handle *identity_handle;
286
287/**
288 * GNS handle
289 */
290static struct GNUNET_GNS_Handle *gns_handle;
291
292/**
293 * Identity Provider
294 */
295static struct GNUNET_RECLAIM_Handle *idp;
296
297/**
298 * @brief struct returned by the initialization function of the plugin
299 */
300struct Plugin
301{
302 const struct GNUNET_CONFIGURATION_Handle *cfg;
303};
304
305/**
306 * OIDC needed variables
307 */
308struct OIDC_Variables
309{
310 /**
311 * The RP client public key
312 */
313 struct GNUNET_IDENTITY_PublicKey client_pkey;
314
315 /**
316 * The OIDC client id of the RP
317 */
318 char *client_id;
319
320 /**
321 * The OIDC redirect uri
322 */
323 char *redirect_uri;
324
325 /**
326 * The list of oidc scopes
327 */
328 char *scope;
329
330 /**
331 * The OIDC state
332 */
333 char *state;
334
335 /**
336 * The OIDC nonce
337 */
338 char *nonce;
339
340 /**
341 * The OIDC claims
342 */
343 char *claims;
344
345 /**
346 * The OIDC response type
347 */
348 char *response_type;
349
350 /**
351 * The identity chosen by the user to login
352 */
353 char *login_identity;
354
355 /**
356 * User cancelled authorization/login
357 */
358 int user_cancelled;
359
360 /**
361 * The PKCE code_challenge
362 */
363 char *code_challenge;
364
365 /**
366 * The PKCE code_verifier
367 */
368 char *code_verifier;
369
370};
371
372/**
373 * The ego list
374 */
375struct EgoEntry
376{
377 /**
378 * DLL
379 */
380 struct EgoEntry *next;
381
382 /**
383 * DLL
384 */
385 struct EgoEntry *prev;
386
387 /**
388 * Ego Identifier
389 */
390 char *identifier;
391
392 /**
393 * Public key string
394 */
395 char *keystring;
396
397 /**
398 * The Ego
399 */
400 struct GNUNET_IDENTITY_Ego *ego;
401};
402
403
404struct RequestHandle
405{
406 /**
407 * DLL
408 */
409 struct RequestHandle *next;
410
411 /**
412 * DLL
413 */
414 struct RequestHandle *prev;
415
416 /**
417 * Selected ego
418 */
419 struct EgoEntry *ego_entry;
420
421 /**
422 * Pointer to ego private key
423 */
424 struct GNUNET_IDENTITY_PrivateKey priv_key;
425
426 /**
427 * OIDC variables
428 */
429 struct OIDC_Variables *oidc;
430
431 /**
432 * GNS lookup op
433 */
434 struct GNUNET_GNS_LookupRequest *gns_op;
435
436 /**
437 * Rest connection
438 */
439 struct GNUNET_REST_RequestHandle *rest_handle;
440
441 /**
442 * Attribute claim list for id_token
443 */
444 struct GNUNET_RECLAIM_AttributeList *attr_idtoken_list;
445
446 /**
447 * Attribute claim list for userinfo
448 */
449 struct GNUNET_RECLAIM_AttributeList *attr_userinfo_list;
450
451 /**
452 * Credentials
453 */
454 struct GNUNET_RECLAIM_CredentialList *credentials;
455
456 /**
457 * Presentations
458 */
459 struct GNUNET_RECLAIM_PresentationList *presentations;
460
461 /**
462 * IDENTITY Operation
463 */
464 struct GNUNET_IDENTITY_Operation *op;
465
466
467 /**
468 * Idp Operation
469 */
470 struct GNUNET_RECLAIM_Operation *idp_op;
471
472 /**
473 * Timeout task for consume
474 */
475 struct GNUNET_SCHEDULER_Task *consume_timeout_op;
476
477 /**
478 * Attribute iterator
479 */
480 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
481
482 /**
483 * Credential iterator
484 */
485 struct GNUNET_RECLAIM_CredentialIterator *cred_it;
486
487
488 /**
489 * Ticket iterator
490 */
491 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
492
493 /**
494 * A ticket
495 */
496 struct GNUNET_RECLAIM_Ticket ticket;
497
498 /**
499 * Desired timeout for the lookup (default is no timeout).
500 */
501 struct GNUNET_TIME_Relative timeout;
502
503 /**
504 * ID of a task associated with the resolution process.
505 */
506 struct GNUNET_SCHEDULER_Task *timeout_task;
507
508 /**
509 * The plugin result processor
510 */
511 GNUNET_REST_ResultProcessor proc;
512
513 /**
514 * The closure of the result processor
515 */
516 void *proc_cls;
517
518 /**
519 * The url
520 */
521 char *url;
522
523 /**
524 * The passed access token
525 */
526 char *access_token;
527
528 /**
529 * The tld for redirect
530 */
531 char *tld;
532
533 /**
534 * The redirect prefix
535 */
536 char *redirect_prefix;
537
538 /**
539 * The redirect suffix
540 */
541 char *redirect_suffix;
542
543 /**
544 * Error response message
545 */
546 char *emsg;
547
548 /**
549 * Error response description
550 */
551 char *edesc;
552
553 /**
554 * Response code
555 */
556 int response_code;
557
558 /**
559 * Public client
560 */
561 int public_client;
562};
563
564/**
565 * DLL
566 */
567static struct RequestHandle *requests_head;
568
569/**
570 * DLL
571 */
572static struct RequestHandle *requests_tail;
573
574
575/**
576 * Cleanup lookup handle
577 * @param handle Handle to clean up
578 */
579static void
580cleanup_handle (struct RequestHandle *handle)
581{
582
583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
584 if (NULL != handle->timeout_task)
585 GNUNET_SCHEDULER_cancel (handle->timeout_task);
586 if (NULL != handle->attr_it)
587 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
588 if (NULL != handle->cred_it)
589 GNUNET_RECLAIM_get_credentials_stop (handle->cred_it);
590 if (NULL != handle->ticket_it)
591 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
592 if (NULL != 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);
596 GNUNET_free (handle->url);
597 GNUNET_free (handle->tld);
598 GNUNET_free (handle->redirect_prefix);
599 GNUNET_free (handle->redirect_suffix);
600 GNUNET_free (handle->emsg);
601 GNUNET_free (handle->edesc);
602 if (NULL != handle->gns_op)
603 GNUNET_GNS_lookup_cancel (handle->gns_op);
604 if (NULL != handle->oidc)
605 {
606 GNUNET_free (handle->oidc->client_id);
607 GNUNET_free (handle->oidc->login_identity);
608 GNUNET_free (handle->oidc->nonce);
609 GNUNET_free (handle->oidc->redirect_uri);
610 GNUNET_free (handle->oidc->response_type);
611 GNUNET_free (handle->oidc->scope);
612 GNUNET_free (handle->oidc->state);
613 if (NULL != handle->oidc->claims)
614 GNUNET_free (handle->oidc->claims);
615 if (NULL != handle->oidc->code_challenge)
616 GNUNET_free (handle->oidc->code_challenge);
617 GNUNET_free (handle->oidc);
618 }
619 if (NULL!=handle->attr_idtoken_list)
620 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_idtoken_list);
621 if (NULL!=handle->attr_userinfo_list)
622 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_userinfo_list);
623 if (NULL!=handle->credentials)
624 GNUNET_RECLAIM_credential_list_destroy (handle->credentials);
625 if (NULL!=handle->presentations)
626 GNUNET_RECLAIM_presentation_list_destroy (handle->presentations);
627 GNUNET_CONTAINER_DLL_remove (requests_head,
628 requests_tail,
629 handle);
630 if (NULL != handle->access_token)
631 GNUNET_free (handle->access_token);
632 GNUNET_free (handle);
633}
634
635
636/**
637 * Task run on error, sends error message. Cleans up everything.
638 *
639 * @param cls the `struct RequestHandle`
640 */
641static void
642do_error (void *cls)
643{
644 struct RequestHandle *handle = cls;
645 struct MHD_Response *resp;
646 char *json_error;
647
648 GNUNET_asprintf (&json_error,
649 "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
650 handle->emsg,
651 (NULL != handle->edesc) ? handle->edesc : "",
652 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
653 (NULL != handle->oidc->state) ? handle->oidc->state : "",
654 (NULL != handle->oidc->state) ? "\"" : "");
655 if (0 == handle->response_code)
656 handle->response_code = MHD_HTTP_BAD_REQUEST;
657 resp = GNUNET_REST_create_response (json_error);
658 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
659 GNUNET_assert (MHD_NO !=
660 MHD_add_response_header (resp,
661 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
662 "Basic"));
663 MHD_add_response_header (resp,
664 MHD_HTTP_HEADER_CONTENT_TYPE,
665 "application/json");
666 handle->proc (handle->proc_cls, resp, handle->response_code);
667 cleanup_handle (handle);
668 GNUNET_free (json_error);
669}
670
671
672/**
673 * Task run on error in userinfo endpoint, sends error header. Cleans up
674 * everything
675 *
676 * @param cls the `struct RequestHandle`
677 */
678static void
679do_userinfo_error (void *cls)
680{
681 struct RequestHandle *handle = cls;
682 struct MHD_Response *resp;
683 char *error;
684
685 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
686 "Error: %s\n", handle->edesc);
687 GNUNET_asprintf (&error,
688 "error=\"%s\", error_description=\"%s\"",
689 handle->emsg,
690 (NULL != handle->edesc) ? handle->edesc : "");
691 resp = GNUNET_REST_create_response ("");
692 GNUNET_assert (MHD_NO !=
693 MHD_add_response_header (resp,
694 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
695 "Bearer"));
696 handle->proc (handle->proc_cls, resp, handle->response_code);
697 cleanup_handle (handle);
698 GNUNET_free (error);
699}
700
701
702/**
703 * Task run on error, sends error message and redirects. Cleans up everything.
704 *
705 * @param cls the `struct RequestHandle`
706 */
707static void
708do_redirect_error (void *cls)
709{
710 struct RequestHandle *handle = cls;
711 struct MHD_Response *resp;
712 char *redirect;
713
714 GNUNET_asprintf (&redirect,
715 "%s?error=%s&error_description=%s%s%s",
716 handle->oidc->redirect_uri,
717 handle->emsg,
718 handle->edesc,
719 (NULL != handle->oidc->state) ? "&state=" : "",
720 (NULL != handle->oidc->state) ? handle->oidc->state : "");
721 resp = GNUNET_REST_create_response ("");
722 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
723 "Location", redirect));
724 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
725 cleanup_handle (handle);
726 GNUNET_free (redirect);
727}
728
729
730/**
731 * Task run on timeout, sends error message. Cleans up everything.
732 *
733 * @param cls the `struct RequestHandle`
734 */
735static void
736do_timeout (void *cls)
737{
738 struct RequestHandle *handle = cls;
739
740 handle->timeout_task = NULL;
741 do_error (handle);
742}
743
744
745/**
746 * Respond to OPTIONS request
747 *
748 * @param con_handle the connection handle
749 * @param url the url
750 * @param cls the RequestHandle
751 */
752static void
753options_cont (struct GNUNET_REST_RequestHandle *con_handle,
754 const char *url,
755 void *cls)
756{
757 struct MHD_Response *resp;
758 struct RequestHandle *handle = cls;
759
760 // For now, independent of path return all options
761 resp = GNUNET_REST_create_response (NULL);
762 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
763 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
764 cleanup_handle (handle);
765 return;
766}
767
768
769/**
770 * Interprets cookie header and pass its identity keystring to handle
771 */
772static void
773cookie_identity_interpretation (struct RequestHandle *handle)
774{
775 struct GNUNET_HashCode cache_key;
776 char *cookies;
777 struct GNUNET_TIME_Absolute current_time, *relog_time;
778 char delimiter[] = "; ";
779 char *tmp_cookies;
780 char *token;
781 char *value;
782
783 // gets identity of login try with cookie
784 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
785 strlen (OIDC_COOKIE_HEADER_KEY),
786 &cache_key);
787 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
788 ->header_param_map,
789 &cache_key))
790 {
791 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
792 return;
793 }
794 // splits cookies and find 'Identity' cookie
795 tmp_cookies =
796 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
797 &cache_key);
798 cookies = GNUNET_strdup (tmp_cookies);
799 token = strtok (cookies, delimiter);
800 handle->oidc->user_cancelled = GNUNET_NO;
801 handle->oidc->login_identity = NULL;
802 if (NULL == token)
803 {
804 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
805 "Unable to parse cookie: %s\n",
806 cookies);
807 GNUNET_free (cookies);
808 return;
809 }
810
811 while (NULL != token)
812 {
813 if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
814 {
815 handle->oidc->user_cancelled = GNUNET_YES;
816 GNUNET_free (cookies);
817 return;
818 }
819 if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
820 break;
821 token = strtok (NULL, delimiter);
822 }
823 if (NULL == token)
824 {
825 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826 "No cookie value to process: %s\n",
827 cookies);
828 GNUNET_free (cookies);
829 return;
830 }
831 GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
832 if (GNUNET_NO ==
833 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
834 {
835 GNUNET_log (
836 GNUNET_ERROR_TYPE_WARNING,
837 "Found cookie `%s', but no corresponding expiration entry present...\n",
838 token);
839 GNUNET_free (cookies);
840 return;
841 }
842 relog_time =
843 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
844 current_time = GNUNET_TIME_absolute_get ();
845 // 30 min after old login -> redirect to login
846 if (current_time.abs_value_us > relog_time->abs_value_us)
847 {
848 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
849 "Found cookie `%s', but it is expired.\n",
850 token);
851 GNUNET_free (cookies);
852 return;
853 }
854 value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
855 GNUNET_assert (NULL != value);
856 handle->oidc->login_identity = GNUNET_strdup (value);
857 GNUNET_free (cookies);
858}
859
860
861/**
862 * Redirects to login page stored in configuration file
863 */
864static void
865login_redirect (void *cls)
866{
867 char *login_base_url;
868 char *new_redirect;
869 char *tmp;
870 struct MHD_Response *resp;
871 struct GNUNET_Buffer buf = { 0 };
872 struct RequestHandle *handle = cls;
873
874 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
875 "reclaim-rest-plugin",
876 "address",
877 &login_base_url))
878 {
879 GNUNET_buffer_write_str (&buf, login_base_url);
880 GNUNET_buffer_write_fstr (&buf,
881 "?%s=%s",
882 OIDC_RESPONSE_TYPE_KEY,
883 handle->oidc->response_type);
884 GNUNET_buffer_write_fstr (&buf,
885 "&%s=%s",
886 OIDC_CLIENT_ID_KEY,
887 handle->oidc->client_id);
888 GNUNET_STRINGS_urlencode (handle->oidc->redirect_uri,
889 strlen (handle->oidc->redirect_uri),
890 &tmp);
891 GNUNET_buffer_write_fstr (&buf,
892 "&%s=%s",
893 OIDC_REDIRECT_URI_KEY,
894 tmp);
895 GNUNET_free (tmp);
896 GNUNET_STRINGS_urlencode (handle->oidc->scope,
897 strlen (handle->oidc->scope),
898 &tmp);
899 GNUNET_buffer_write_fstr (&buf,
900 "&%s=%s",
901 OIDC_SCOPE_KEY,
902 tmp);
903 GNUNET_free (tmp);
904 if (NULL != handle->oidc->state)
905 {
906 GNUNET_STRINGS_urlencode (handle->oidc->state,
907 strlen (handle->oidc->state),
908 &tmp);
909 GNUNET_buffer_write_fstr (&buf,
910 "&%s=%s",
911 OIDC_STATE_KEY,
912 handle->oidc->state);
913 GNUNET_free (tmp);
914 }
915 if (NULL != handle->oidc->code_challenge)
916 {
917 GNUNET_buffer_write_fstr (&buf,
918 "&%s=%s",
919 OIDC_CODE_CHALLENGE_KEY,
920 handle->oidc->code_challenge);
921 }
922 if (NULL != handle->oidc->nonce)
923 {
924 GNUNET_buffer_write_fstr (&buf,
925 "&%s=%s",
926 OIDC_NONCE_KEY,
927 handle->oidc->nonce);
928 }
929 if (NULL != handle->oidc->claims)
930 {
931 GNUNET_STRINGS_urlencode (handle->oidc->claims,
932 strlen (handle->oidc->claims),
933 &tmp);
934 GNUNET_buffer_write_fstr (&buf,
935 "&%s=%s",
936 OIDC_CLAIMS_KEY,
937 tmp);
938 GNUNET_free (tmp);
939 }
940 new_redirect = GNUNET_buffer_reap_str (&buf);
941 resp = GNUNET_REST_create_response ("");
942 MHD_add_response_header (resp, "Location", new_redirect);
943 GNUNET_free (login_base_url);
944 }
945 else
946 {
947 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
948 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
949 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
950 GNUNET_SCHEDULER_add_now (&do_error, handle);
951 return;
952 }
953 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
954 GNUNET_free (new_redirect);
955 cleanup_handle (handle);
956}
957
958
959/**
960 * Does internal server error when iteration failed.
961 */
962static void
963oidc_iteration_error (void *cls)
964{
965 struct RequestHandle *handle = cls;
966
967 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
968 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
969 GNUNET_SCHEDULER_add_now (&do_error, handle);
970}
971
972
973/**
974 * Issues ticket and redirects to relying party with the authorization code as
975 * parameter. Otherwise redirects with error
976 */
977static void
978oidc_ticket_issue_cb (void *cls,
979 const struct GNUNET_RECLAIM_Ticket *ticket,
980 const struct
981 GNUNET_RECLAIM_PresentationList *presentation)
982{
983 struct RequestHandle *handle = cls;
984 struct MHD_Response *resp;
985 char *ticket_str;
986 char *redirect_uri;
987 char *code_string;
988
989 handle->idp_op = NULL;
990 if (NULL == ticket)
991 {
992 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
993 handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
994 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
995 return;
996 }
997 handle->ticket = *ticket;
998 ticket_str =
999 GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
1000 sizeof(struct GNUNET_RECLAIM_Ticket));
1001 code_string = OIDC_build_authz_code (&handle->priv_key,
1002 &handle->ticket,
1003 handle->attr_idtoken_list,
1004 presentation,
1005 handle->oidc->nonce,
1006 handle->oidc->code_challenge);
1007 if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
1008 (NULL != handle->tld))
1009 {
1010 GNUNET_asprintf (&redirect_uri,
1011 "%s.%s/%s%s%s=%s&state=%s",
1012 handle->redirect_prefix,
1013 handle->tld,
1014 handle->redirect_suffix,
1015 (NULL == strchr (handle->redirect_suffix, '?') ? "?" :
1016 "&"),
1017 handle->oidc->response_type,
1018 code_string,
1019 handle->oidc->state);
1020 }
1021 else
1022 {
1023 GNUNET_asprintf (&redirect_uri,
1024 "%s%s%s=%s&state=%s",
1025 handle->oidc->redirect_uri,
1026 (NULL == strchr (handle->oidc->redirect_uri, '?') ? "?" :
1027 "&"),
1028 handle->oidc->response_type,
1029 code_string,
1030 handle->oidc->state);
1031 }
1032 resp = GNUNET_REST_create_response ("");
1033 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1034 "Location", redirect_uri));
1035 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1036 cleanup_handle (handle);
1037 GNUNET_free (redirect_uri);
1038 GNUNET_free (ticket_str);
1039 GNUNET_free (code_string);
1040}
1041
1042
1043static struct GNUNET_RECLAIM_AttributeList*
1044attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a,
1045 struct GNUNET_RECLAIM_AttributeList *list_b)
1046{
1047 struct GNUNET_RECLAIM_AttributeList *merged_list;
1048 struct GNUNET_RECLAIM_AttributeListEntry *le_a;
1049 struct GNUNET_RECLAIM_AttributeListEntry *le_b;
1050 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1051
1052 merged_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1053 for (le_a = list_a->list_head; NULL != le_a; le_a = le_a->next)
1054 {
1055 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1056 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_a->attribute->name,
1057 &le_a->attribute->
1058 credential,
1059 le_a->attribute->type,
1060 le_a->attribute->data,
1061 le_a->attribute->data_size);
1062 le_m->attribute->id = le_a->attribute->id;
1063 le_m->attribute->flag = le_a->attribute->flag;
1064 le_m->attribute->credential = le_a->attribute->credential;
1065 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1066 merged_list->list_tail,
1067 le_m);
1068 }
1069 le_m = NULL;
1070 for (le_b = list_b->list_head; NULL != le_b; le_b = le_b->next)
1071 {
1072 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1073 {
1074 if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&le_m->attribute->id,
1075 &le_b->attribute->id))
1076 break; /** Attribute already in list **/
1077 }
1078 if (NULL != le_m)
1079 continue; /** Attribute already in list **/
1080 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1081 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_b->attribute->name,
1082 &le_b->attribute->
1083 credential,
1084 le_b->attribute->type,
1085 le_b->attribute->data,
1086 le_b->attribute->data_size);
1087 le_m->attribute->id = le_b->attribute->id;
1088 le_m->attribute->flag = le_b->attribute->flag;
1089 le_m->attribute->credential = le_b->attribute->credential;
1090 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1091 merged_list->list_tail,
1092 le_m);
1093 }
1094 return merged_list;
1095}
1096
1097
1098static void
1099oidc_cred_collect_finished_cb (void *cls)
1100{
1101 struct RequestHandle *handle = cls;
1102 struct GNUNET_RECLAIM_AttributeList *merged_list;
1103 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1104
1105 handle->cred_it = NULL;
1106 merged_list = attribute_list_merge (handle->attr_idtoken_list,
1107 handle->attr_userinfo_list);
1108 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1110 "List Attribute in ticket to issue: %s\n",
1111 le_m->attribute->name);
1112 handle->idp_op = GNUNET_RECLAIM_ticket_issue (idp,
1113 &handle->priv_key,
1114 &handle->oidc->client_pkey,
1115 merged_list,
1116 &oidc_ticket_issue_cb,
1117 handle);
1118 GNUNET_RECLAIM_attribute_list_destroy (merged_list);
1119}
1120
1121
1122/**
1123 * Collects all attributes for an ego if in scope parameter
1124 */
1125static void
1126oidc_cred_collect (void *cls,
1127 const struct GNUNET_IDENTITY_PublicKey *identity,
1128 const struct GNUNET_RECLAIM_Credential *cred)
1129{
1130 struct RequestHandle *handle = cls;
1131 struct GNUNET_RECLAIM_AttributeListEntry *le;
1132 struct GNUNET_RECLAIM_CredentialListEntry *ale;
1133
1134 for (ale = handle->credentials->list_head; NULL != ale; ale = ale->next)
1135 {
1136 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->credential->id,
1137 &cred->id))
1138 continue;
1139 /** Credential already in list **/
1140 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1141 return;
1142 }
1143
1144 for (le = handle->attr_idtoken_list->list_head; NULL != le; le = le->next)
1145 {
1146 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->credential,
1147 &cred->id))
1148 continue;
1149 /** Credential matches for attribute, add **/
1150 ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry);
1151 ale->credential = GNUNET_RECLAIM_credential_new (cred->name,
1152 cred->type,
1153 cred->data,
1154 cred->data_size);
1155 GNUNET_CONTAINER_DLL_insert (handle->credentials->list_head,
1156 handle->credentials->list_tail,
1157 ale);
1158 }
1159 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1160}
1161
1162
1163static void
1164oidc_attr_collect_finished_cb (void *cls)
1165{
1166 struct RequestHandle *handle = cls;
1167
1168 handle->attr_it = NULL;
1169 handle->ticket_it = NULL;
1170 if (NULL == handle->attr_idtoken_list->list_head)
1171 {
1172 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1173 handle->edesc = GNUNET_strdup ("The requested scope is not available.");
1174 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1175 return;
1176 }
1177 handle->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList);
1178 handle->cred_it =
1179 GNUNET_RECLAIM_get_credentials_start (idp,
1180 &handle->priv_key,
1181 &oidc_iteration_error,
1182 handle,
1183 &oidc_cred_collect,
1184 handle,
1185 &oidc_cred_collect_finished_cb,
1186 handle);
1187
1188}
1189
1190
1191static int
1192attr_in_claims_request (struct RequestHandle *handle,
1193 const char *attr_name,
1194 const char *claims_parameter)
1195{
1196 int ret = GNUNET_NO;
1197 json_t *root;
1198 json_error_t error;
1199 json_t *claims_j;
1200 const char *key;
1201 json_t *value;
1202
1203 /** Check if attribute is requested through a scope **/
1204 if (GNUNET_YES == OIDC_check_scopes_for_claim_request (handle->oidc->scope,
1205 attr_name))
1206 return GNUNET_YES;
1207
1208 /** Try claims parameter if not in scope */
1209 if (NULL != handle->oidc->claims)
1210 {
1211 root = json_loads (handle->oidc->claims, JSON_DECODE_ANY, &error);
1212 claims_j = json_object_get (root, claims_parameter);
1213 /* obj is a JSON object */
1214 if (NULL != claims_j)
1215 {
1216 json_object_foreach (claims_j, key, value) {
1217 if (0 != strcmp (attr_name, key))
1218 continue;
1219 ret = GNUNET_YES;
1220 break;
1221 }
1222 }
1223 json_decref (root);
1224 }
1225 return ret;
1226}
1227
1228
1229static int
1230attr_in_idtoken_request (struct RequestHandle *handle,
1231 const char *attr_name)
1232{
1233 return attr_in_claims_request (handle, attr_name, "id_token");
1234}
1235
1236
1237static int
1238attr_in_userinfo_request (struct RequestHandle *handle,
1239 const char *attr_name)
1240{
1241 return attr_in_claims_request (handle, attr_name, "userinfo");
1242}
1243
1244
1245/**
1246 * Collects all attributes for an ego if in scope parameter
1247 */
1248static void
1249oidc_attr_collect (void *cls,
1250 const struct GNUNET_IDENTITY_PublicKey *identity,
1251 const struct GNUNET_RECLAIM_Attribute *attr)
1252{
1253 struct RequestHandle *handle = cls;
1254 struct GNUNET_RECLAIM_AttributeListEntry *le;
1255 if (GNUNET_YES == attr_in_idtoken_request (handle, attr->name))
1256 {
1257 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1258 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1259 &attr->credential,
1260 attr->type,
1261 attr->data,
1262 attr->data_size);
1263 le->attribute->id = attr->id;
1264 le->attribute->flag = attr->flag;
1265 le->attribute->credential = attr->credential;
1266 GNUNET_CONTAINER_DLL_insert (handle->attr_idtoken_list->list_head,
1267 handle->attr_idtoken_list->list_tail,
1268 le);
1269 }
1270 if (GNUNET_YES == attr_in_userinfo_request (handle, attr->name))
1271 {
1272 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1273 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1274 &attr->credential,
1275 attr->type,
1276 attr->data,
1277 attr->data_size);
1278 le->attribute->id = attr->id;
1279 le->attribute->flag = attr->flag;
1280 le->attribute->credential = attr->credential;
1281 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
1282 handle->attr_userinfo_list->list_tail,
1283 le);
1284 }
1285
1286 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1287}
1288
1289
1290/**
1291 * Checks time and cookie and redirects accordingly
1292 */
1293static void
1294code_redirect (void *cls)
1295{
1296 struct RequestHandle *handle = cls;
1297 struct GNUNET_TIME_Absolute current_time;
1298 struct GNUNET_TIME_Absolute *relog_time;
1299 struct GNUNET_IDENTITY_PublicKey pubkey;
1300 struct GNUNET_IDENTITY_PublicKey ego_pkey;
1301 struct GNUNET_HashCode cache_key;
1302 char *identity_cookie;
1303
1304 GNUNET_asprintf (&identity_cookie,
1305 "Identity=%s",
1306 handle->oidc->login_identity);
1307 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1308 GNUNET_free (identity_cookie);
1309 // No login time for identity -> redirect to login
1310 if (GNUNET_YES ==
1311 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1312 {
1313 relog_time =
1314 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1315 current_time = GNUNET_TIME_absolute_get ();
1316 // 30 min after old login -> redirect to login
1317 if (current_time.abs_value_us <= relog_time->abs_value_us)
1318 {
1319 if (GNUNET_OK !=
1320 GNUNET_IDENTITY_public_key_from_string (handle->oidc
1321 ->login_identity,
1322 &pubkey))
1323 {
1324 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1325 handle->edesc =
1326 GNUNET_strdup ("The cookie of a login identity is not valid");
1327 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1328 return;
1329 }
1330 // iterate over egos and compare their public key
1331 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
1332 handle->ego_entry = handle->ego_entry->next)
1333 {
1334 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1335 if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1336 {
1337 handle->priv_key =
1338 *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1339 handle->attr_idtoken_list =
1340 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1341 handle->attr_userinfo_list =
1342 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1343 handle->attr_it =
1344 GNUNET_RECLAIM_get_attributes_start (idp,
1345 &handle->priv_key,
1346 &oidc_iteration_error,
1347 handle,
1348 &oidc_attr_collect,
1349 handle,
1350 &oidc_attr_collect_finished_cb,
1351 handle);
1352 return;
1353 }
1354 }
1355 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1356 return;
1357 }
1358 }
1359}
1360
1361
1362static void
1363build_redirect (void *cls)
1364{
1365 struct RequestHandle *handle = cls;
1366 struct MHD_Response *resp;
1367 char *redirect_uri;
1368
1369 if (GNUNET_YES == handle->oidc->user_cancelled)
1370 {
1371 if ((NULL != handle->redirect_prefix) &&
1372 (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1373 {
1374 GNUNET_asprintf (&redirect_uri,
1375 "%s.%s/%s?error=%s&error_description=%s&state=%s",
1376 handle->redirect_prefix,
1377 handle->tld,
1378 handle->redirect_suffix,
1379 "access_denied",
1380 "User denied access",
1381 handle->oidc->state);
1382 }
1383 else
1384 {
1385 GNUNET_asprintf (&redirect_uri,
1386 "%s?error=%s&error_description=%s&state=%s",
1387 handle->oidc->redirect_uri,
1388 "access_denied",
1389 "User denied access",
1390 handle->oidc->state);
1391 }
1392 resp = GNUNET_REST_create_response ("");
1393 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1394 "Location",
1395 redirect_uri));
1396 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1397 cleanup_handle (handle);
1398 GNUNET_free (redirect_uri);
1399 return;
1400 }
1401 GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1402}
1403
1404
1405static void
1406lookup_redirect_uri_result (void *cls,
1407 uint32_t rd_count,
1408 const struct GNUNET_GNSRECORD_Data *rd)
1409{
1410 struct RequestHandle *handle = cls;
1411 char *tmp;
1412 char *tmp_key_str;
1413 char *pos;
1414 struct GNUNET_IDENTITY_PublicKey redirect_zone;
1415
1416 handle->gns_op = NULL;
1417 if (0 == rd_count)
1418 {
1419 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1420 handle->edesc =
1421 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1422 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1423 return;
1424 }
1425 for (int i = 0; i < rd_count; i++)
1426 {
1427 if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1428 continue;
1429 if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1430 continue;
1431 tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1432 if (NULL == strstr (tmp, handle->oidc->client_id))
1433 {
1434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1435 "Redirect uri %s does not contain client_id %s\n",
1436 tmp,
1437 handle->oidc->client_id);
1438 }
1439 else
1440 {
1441 pos = strrchr (tmp, (unsigned char) '.');
1442 if (NULL == pos)
1443 {
1444 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1445 "Redirect uri %s contains client_id but is malformed\n",
1446 tmp);
1447 GNUNET_free (tmp);
1448 continue;
1449 }
1450 *pos = '\0';
1451 handle->redirect_prefix = GNUNET_strdup (tmp);
1452 tmp_key_str = pos + 1;
1453 pos = strchr (tmp_key_str, (unsigned char) '/');
1454 if (NULL == pos)
1455 {
1456 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1457 "Redirect uri %s contains client_id but is malformed\n",
1458 tmp);
1459 GNUNET_free (tmp);
1460 continue;
1461 }
1462 *pos = '\0';
1463 handle->redirect_suffix = GNUNET_strdup (pos + 1);
1464
1465 GNUNET_STRINGS_string_to_data (tmp_key_str,
1466 strlen (tmp_key_str),
1467 &redirect_zone,
1468 sizeof(redirect_zone));
1469 }
1470 GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1471 GNUNET_free (tmp);
1472 return;
1473 }
1474 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1475 handle->edesc =
1476 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1477 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1478}
1479
1480
1481/**
1482 * Initiate redirect back to client.
1483 */
1484static void
1485client_redirect (void *cls)
1486{
1487 struct RequestHandle *handle = cls;
1488
1489 /* Lookup client redirect uri to verify request */
1490 handle->gns_op =
1491 GNUNET_GNS_lookup (gns_handle,
1492 GNUNET_GNS_EMPTY_LABEL_AT,
1493 &handle->oidc->client_pkey,
1494 GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1495 GNUNET_GNS_LO_DEFAULT,
1496 &lookup_redirect_uri_result,
1497 handle);
1498}
1499
1500
1501static char *
1502get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1503{
1504 struct GNUNET_HashCode hc;
1505 char *value;
1506 char *res;
1507
1508 GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1509 if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1510 ->url_param_map,
1511 &hc))
1512 return NULL;
1513 value =
1514 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1515 if (NULL == value)
1516 return NULL;
1517 GNUNET_STRINGS_urldecode (value, strlen (value), &res);
1518 return res;
1519}
1520
1521
1522/**
1523 * Iteration over all results finished, build final
1524 * response.
1525 *
1526 * @param cls the `struct RequestHandle`
1527 */
1528static void
1529build_authz_response (void *cls)
1530{
1531 struct RequestHandle *handle = cls;
1532 struct GNUNET_HashCode cache_key;
1533
1534 char *expected_scope;
1535 char delimiter[] = " ";
1536 int number_of_ignored_parameter, iterator;
1537
1538
1539 // REQUIRED value: redirect_uri
1540 handle->oidc->redirect_uri =
1541 get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1542 if (NULL == handle->oidc->redirect_uri)
1543 {
1544 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1545 handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1546 GNUNET_SCHEDULER_add_now (&do_error, handle);
1547 return;
1548 }
1549
1550 // REQUIRED value: response_type
1551 handle->oidc->response_type =
1552 get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1553 if (NULL == handle->oidc->response_type)
1554 {
1555 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1556 handle->edesc = GNUNET_strdup ("missing parameter response_type");
1557 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1558 return;
1559 }
1560
1561 // REQUIRED value: scope
1562 handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1563 if (NULL == handle->oidc->scope)
1564 {
1565 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1566 handle->edesc = GNUNET_strdup ("missing parameter scope");
1567 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1568 return;
1569 }
1570
1571 // OPTIONAL value: nonce
1572 handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1573
1574 // OPTIONAL value: claims
1575 handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1576
1577 // TODO check other values if needed
1578 number_of_ignored_parameter =
1579 sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1580 for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1581 {
1582 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1583 strlen (OIDC_ignored_parameter_array[iterator]),
1584 &cache_key);
1585 if (GNUNET_YES ==
1586 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1587 ->url_param_map,
1588 &cache_key))
1589 {
1590 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1591 GNUNET_asprintf (&handle->edesc,
1592 "Server will not handle parameter: %s",
1593 OIDC_ignored_parameter_array[iterator]);
1594 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1595 return;
1596 }
1597 }
1598
1599 // We only support authorization code flows.
1600 if (0 != strcmp (handle->oidc->response_type,
1601 OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1602 {
1603 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1604 handle->edesc = GNUNET_strdup ("The authorization server does not support "
1605 "obtaining this authorization code.");
1606 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1607 return;
1608 }
1609
1610 // Checks if scope contains 'openid'
1611 expected_scope = GNUNET_strdup (handle->oidc->scope);
1612 char *test;
1613 test = strtok (expected_scope, delimiter);
1614 while (NULL != test)
1615 {
1616 if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1617 break;
1618 test = strtok (NULL, delimiter);
1619 }
1620 if (NULL == test)
1621 {
1622 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1623 handle->edesc =
1624 GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1625 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1626 GNUNET_free (expected_scope);
1627 return;
1628 }
1629
1630 GNUNET_free (expected_scope);
1631 if ((NULL == handle->oidc->login_identity) &&
1632 (GNUNET_NO == handle->oidc->user_cancelled))
1633 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1634 else
1635 GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1636}
1637
1638
1639/**
1640 * Iterate over tlds in config
1641 */
1642static void
1643tld_iter (void *cls, const char *section, const char *option, const char *value)
1644{
1645 struct RequestHandle *handle = cls;
1646 struct GNUNET_IDENTITY_PublicKey pkey;
1647
1648 if (GNUNET_OK !=
1649 GNUNET_IDENTITY_public_key_from_string (value, &pkey))
1650 {
1651 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1652 return;
1653 }
1654 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1655 handle->tld = GNUNET_strdup (option + 1);
1656}
1657
1658
1659/**
1660 * Responds to authorization GET and url-encoded POST request
1661 *
1662 * @param con_handle the connection handle
1663 * @param url the url
1664 * @param cls the RequestHandle
1665 */
1666static void
1667authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1668 const char *url,
1669 void *cls)
1670{
1671 struct RequestHandle *handle = cls;
1672 struct EgoEntry *tmp_ego;
1673 const struct GNUNET_IDENTITY_PrivateKey *priv_key;
1674 struct GNUNET_IDENTITY_PublicKey pkey;
1675
1676 cookie_identity_interpretation (handle);
1677
1678 // RECOMMENDED value: state - REQUIRED for answers
1679 handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1680
1681 // REQUIRED value: client_id
1682 handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1683 if (NULL == handle->oidc->client_id)
1684 {
1685 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1686 handle->edesc = GNUNET_strdup ("missing parameter client_id");
1687 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1688 GNUNET_SCHEDULER_add_now (&do_error, handle);
1689 return;
1690 }
1691
1692 // OPTIONAL value: code_challenge
1693 handle->oidc->code_challenge = get_url_parameter_copy (handle,
1694 OIDC_CODE_CHALLENGE_KEY);
1695 if (NULL == handle->oidc->code_challenge)
1696 {
1697 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1698 "OAuth authorization request does not contain PKCE parameters!\n");
1699 }
1700
1701 if (GNUNET_OK !=
1702 GNUNET_IDENTITY_public_key_from_string (handle->oidc->client_id,
1703 &handle->oidc->client_pkey))
1704 {
1705 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1706 handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1707 "authorization code using this method.");
1708 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1709 GNUNET_SCHEDULER_add_now (&do_error, handle);
1710 return;
1711 }
1712
1713 // If we know this identity, translated the corresponding TLD
1714 // TODO: We might want to have a reverse lookup functionality for TLDs?
1715 for (tmp_ego = ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1716 {
1717 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1718 GNUNET_IDENTITY_key_get_public (priv_key, &pkey);
1719 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1720 {
1721 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1722 handle->ego_entry = ego_tail;
1723 }
1724 }
1725 if (NULL == handle->tld)
1726 GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1727 if (NULL == handle->tld)
1728 handle->tld = GNUNET_strdup (handle->oidc->client_id);
1729 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1730}
1731
1732
1733/**
1734 * Combines an identity with a login time and responds OK to login request
1735 *
1736 * @param con_handle the connection handle
1737 * @param url the url
1738 * @param cls the RequestHandle
1739 */
1740static void
1741login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1742 const char *url,
1743 void *cls)
1744{
1745 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1746 struct RequestHandle *handle = cls;
1747 struct GNUNET_HashCode cache_key;
1748 struct GNUNET_TIME_Absolute *current_time;
1749 struct GNUNET_TIME_Absolute *last_time;
1750 char *cookie;
1751 char *header_val;
1752 json_t *root;
1753 json_error_t error;
1754 json_t *identity;
1755 char term_data[handle->rest_handle->data_size + 1];
1756
1757 term_data[handle->rest_handle->data_size] = '\0';
1758 GNUNET_memcpy (term_data,
1759 handle->rest_handle->data,
1760 handle->rest_handle->data_size);
1761 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1762 identity = json_object_get (root, "identity");
1763 if (! json_is_string (identity))
1764 {
1765 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1766 "Error parsing json string from %s\n",
1767 term_data);
1768 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1769 json_decref (root);
1770 cleanup_handle (handle);
1771 return;
1772 }
1773 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1774 GNUNET_asprintf (&header_val,
1775 "%s;Max-Age=%d",
1776 cookie,
1777 OIDC_COOKIE_EXPIRATION);
1778 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1779 "Set-Cookie", header_val));
1780 GNUNET_assert (MHD_NO !=
1781 MHD_add_response_header (resp,
1782 "Access-Control-Allow-Methods",
1783 "POST"));
1784 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1785
1786 if (0 != strcmp (json_string_value (identity), "Denied"))
1787 {
1788 current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1789 *current_time = GNUNET_TIME_relative_to_absolute (
1790 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1791 OIDC_COOKIE_EXPIRATION));
1792 last_time =
1793 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1794 GNUNET_free (last_time);
1795 GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1796 &cache_key,
1797 current_time,
1798 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1799 }
1800 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1801 GNUNET_free (cookie);
1802 GNUNET_free (header_val);
1803 json_decref (root);
1804 cleanup_handle (handle);
1805}
1806
1807
1808static int
1809parse_credentials_basic_auth (struct RequestHandle *handle,
1810 char **client_id,
1811 char **client_secret)
1812{
1813 struct GNUNET_HashCode cache_key;
1814 char *authorization;
1815 char *credentials;
1816 char *basic_authorization;
1817 char *client_id_tmp;
1818 char *pass;
1819
1820 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1821 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1822 &cache_key);
1823 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1824 ->header_param_map,
1825 &cache_key))
1826 return GNUNET_SYSERR;
1827 authorization =
1828 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1829 &cache_key);
1830
1831 // split header in "Basic" and [content]
1832 credentials = strtok (authorization, " ");
1833 if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1834 return GNUNET_SYSERR;
1835 credentials = strtok (NULL, " ");
1836 if (NULL == credentials)
1837 return GNUNET_SYSERR;
1838 GNUNET_STRINGS_base64_decode (credentials,
1839 strlen (credentials),
1840 (void **) &basic_authorization);
1841
1842 if (NULL == basic_authorization)
1843 return GNUNET_SYSERR;
1844 client_id_tmp = strtok (basic_authorization, ":");
1845 if (NULL == client_id_tmp)
1846 {
1847 GNUNET_free (basic_authorization);
1848 return GNUNET_SYSERR;
1849 }
1850 pass = strtok (NULL, ":");
1851 if (NULL == pass)
1852 {
1853 GNUNET_free (basic_authorization);
1854 return GNUNET_SYSERR;
1855 }
1856 *client_id = strdup (client_id_tmp);
1857 *client_secret = strdup (pass);
1858 GNUNET_free (basic_authorization);
1859 return GNUNET_OK;
1860}
1861
1862
1863static int
1864parse_credentials_post_body (struct RequestHandle *handle,
1865 char **client_id,
1866 char **client_secret)
1867{
1868 struct GNUNET_HashCode cache_key;
1869 char *client_id_tmp;
1870 char *pass;
1871
1872 GNUNET_CRYPTO_hash ("client_id",
1873 strlen ("client_id"),
1874 &cache_key);
1875 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1876 ->url_param_map,
1877 &cache_key))
1878 return GNUNET_SYSERR;
1879 client_id_tmp = GNUNET_CONTAINER_multihashmap_get (
1880 handle->rest_handle->url_param_map,
1881 &cache_key);
1882 if (NULL == client_id_tmp)
1883 return GNUNET_SYSERR;
1884 *client_id = strdup (client_id_tmp);
1885 GNUNET_CRYPTO_hash ("client_secret",
1886 strlen ("client_secret"),
1887 &cache_key);
1888 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1889 ->url_param_map,
1890 &cache_key))
1891 {
1892 GNUNET_free (*client_id);
1893 *client_id = NULL;
1894 return GNUNET_SYSERR;
1895 }
1896 pass = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1897 &cache_key);
1898 if (NULL == pass)
1899 {
1900 GNUNET_free (*client_id);
1901 *client_id = NULL;
1902 return GNUNET_SYSERR;
1903 }
1904 *client_secret = strdup (pass);
1905 return GNUNET_OK;
1906}
1907
1908
1909static int
1910check_authorization (struct RequestHandle *handle,
1911 struct GNUNET_IDENTITY_PublicKey *cid)
1912{
1913 char *expected_pass;
1914 char *received_cid;
1915 char *received_cpw;
1916 char *pkce_cv;
1917
1918 if (GNUNET_OK == parse_credentials_basic_auth (handle,
1919 &received_cid,
1920 &received_cpw))
1921 {
1922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1923 "Received client credentials in HTTP AuthZ header\n");
1924 }
1925 else if (GNUNET_OK == parse_credentials_post_body (handle,
1926 &received_cid,
1927 &received_cpw))
1928 {
1929 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1930 "Received client credentials in POST body\n");
1931 }
1932 else
1933 {
1934 /** Allow public clients with PKCE **/
1935 pkce_cv = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
1936 if (NULL == pkce_cv)
1937 {
1938 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1939 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1940 return GNUNET_SYSERR;
1941 }
1942 handle->public_client = GNUNET_YES;
1943 GNUNET_free (pkce_cv);
1944 received_cid = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1945 GNUNET_STRINGS_string_to_data (received_cid,
1946 strlen (received_cid),
1947 cid,
1948 sizeof(struct GNUNET_IDENTITY_PublicKey));
1949 GNUNET_free (received_cid);
1950 return GNUNET_OK;
1951
1952 }
1953
1954 // check client password
1955 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1956 "reclaim-rest-plugin",
1957 "OIDC_CLIENT_SECRET",
1958 &expected_pass))
1959 {
1960 if (0 != strcmp (expected_pass, received_cpw))
1961 {
1962 GNUNET_free (expected_pass);
1963 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1964 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1965 GNUNET_free (received_cpw);
1966 GNUNET_free (received_cid);
1967 return GNUNET_SYSERR;
1968 }
1969 GNUNET_free (expected_pass);
1970 }
1971 else
1972 {
1973 GNUNET_free (received_cpw);
1974 GNUNET_free (received_cid);
1975 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1976 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1977 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1978 return GNUNET_SYSERR;
1979 }
1980 // check client_id
1981 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
1982 handle->ego_entry = handle->ego_entry->next)
1983 {
1984 if (0 == strcmp (handle->ego_entry->keystring, received_cid))
1985 break;
1986 }
1987 if (NULL == handle->ego_entry)
1988 {
1989 GNUNET_free (received_cpw);
1990 GNUNET_free (received_cid);
1991 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1992 handle->response_code = MHD_HTTP_UNAUTHORIZED;
1993 return GNUNET_SYSERR;
1994 }
1995 GNUNET_STRINGS_string_to_data (received_cid,
1996 strlen (received_cid),
1997 cid,
1998 sizeof(struct GNUNET_IDENTITY_PublicKey));
1999
2000 GNUNET_free (received_cpw);
2001 GNUNET_free (received_cid);
2002 return GNUNET_OK;
2003}
2004
2005
2006const struct EgoEntry *
2007find_ego (struct RequestHandle *handle,
2008 struct GNUNET_IDENTITY_PublicKey *test_key)
2009{
2010 struct EgoEntry *ego_entry;
2011 struct GNUNET_IDENTITY_PublicKey pub_key;
2012
2013 for (ego_entry = ego_head; NULL != ego_entry;
2014 ego_entry = ego_entry->next)
2015 {
2016 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
2017 if (0 == GNUNET_memcmp (&pub_key, test_key))
2018 return ego_entry;
2019 }
2020 return NULL;
2021}
2022
2023
2024/**
2025 * Responds to token url-encoded POST request
2026 *
2027 * @param con_handle the connection handle
2028 * @param url the url
2029 * @param cls the RequestHandle
2030 */
2031static void
2032token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2033 const char *url,
2034 void *cls)
2035{
2036 struct RequestHandle *handle = cls;
2037 const struct EgoEntry *ego_entry;
2038 struct GNUNET_TIME_Relative expiration_time;
2039 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2040 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2041 struct GNUNET_RECLAIM_Ticket ticket;
2042 struct GNUNET_IDENTITY_PublicKey cid;
2043 struct GNUNET_HashCode cache_key;
2044 struct MHD_Response *resp;
2045 char *grant_type;
2046 char *code;
2047 char *json_response;
2048 char *id_token;
2049 char *access_token;
2050 char *jwt_secret;
2051 char *nonce = NULL;
2052 char *code_verifier;
2053
2054 /*
2055 * Check Authorization
2056 */
2057 if (GNUNET_SYSERR == check_authorization (handle, &cid))
2058 {
2059 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2060 "OIDC authorization for token endpoint failed\n");
2061 GNUNET_SCHEDULER_add_now (&do_error, handle);
2062 return;
2063 }
2064
2065 /*
2066 * Check parameter
2067 */
2068
2069 // TODO Do not allow multiple equal parameter names
2070 // REQUIRED grant_type
2071 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
2072 strlen (OIDC_GRANT_TYPE_KEY),
2073 &cache_key);
2074 grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
2075 if (NULL == grant_type)
2076 {
2077 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2078 handle->edesc = GNUNET_strdup ("missing parameter grant_type");
2079 handle->response_code = MHD_HTTP_BAD_REQUEST;
2080 GNUNET_SCHEDULER_add_now (&do_error, handle);
2081 return;
2082 }
2083
2084 // Check parameter grant_type == "authorization_code"
2085 if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
2086 {
2087 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
2088 handle->response_code = MHD_HTTP_BAD_REQUEST;
2089 GNUNET_free (grant_type);
2090 GNUNET_SCHEDULER_add_now (&do_error, handle);
2091 return;
2092 }
2093 GNUNET_free (grant_type);
2094 // REQUIRED code
2095 code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
2096 if (NULL == code)
2097 {
2098 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2099 handle->edesc = GNUNET_strdup ("missing parameter code");
2100 handle->response_code = MHD_HTTP_BAD_REQUEST;
2101 GNUNET_SCHEDULER_add_now (&do_error, handle);
2102 return;
2103 }
2104 ego_entry = find_ego (handle, &cid);
2105 if (NULL == ego_entry)
2106 {
2107 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2108 handle->edesc = GNUNET_strdup ("Unknown client");
2109 handle->response_code = MHD_HTTP_BAD_REQUEST;
2110 GNUNET_free (code);
2111 GNUNET_SCHEDULER_add_now (&do_error, handle);
2112 return;
2113 }
2114
2115 // REQUIRED code verifier
2116 code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2117 if (NULL == code_verifier)
2118 {
2119 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2120 "OAuth authorization request does not contain PKCE parameters!\n");
2121
2122 }
2123
2124 // decode code
2125 if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket,
2126 &cl, &pl, &nonce,
2127 OIDC_VERIFICATION_DEFAULT))
2128 {
2129 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2130 handle->edesc = GNUNET_strdup ("invalid code");
2131 handle->response_code = MHD_HTTP_BAD_REQUEST;
2132 GNUNET_free (code);
2133 if (NULL != code_verifier)
2134 GNUNET_free (code_verifier);
2135 GNUNET_SCHEDULER_add_now (&do_error, handle);
2136 return;
2137 }
2138 if (NULL != code_verifier)
2139 GNUNET_free (code_verifier);
2140
2141 // create jwt
2142 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
2143 "reclaim-rest-plugin",
2144 "expiration_time",
2145 &expiration_time))
2146 {
2147 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2148 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2149 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2150 GNUNET_free (code);
2151 if (NULL != nonce)
2152 GNUNET_free (nonce);
2153 GNUNET_RECLAIM_attribute_list_destroy (cl);
2154 GNUNET_RECLAIM_presentation_list_destroy (pl);
2155 GNUNET_SCHEDULER_add_now (&do_error, handle);
2156 return;
2157 }
2158
2159
2160 // TODO OPTIONAL acr,amr,azp
2161 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2162 "reclaim-rest-plugin",
2163 "jwt_secret",
2164 &jwt_secret))
2165 {
2166 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2167 handle->edesc = GNUNET_strdup ("No signing secret configured!");
2168 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2169 GNUNET_free (code);
2170 GNUNET_RECLAIM_attribute_list_destroy (cl);
2171 GNUNET_RECLAIM_presentation_list_destroy (pl);
2172 if (NULL != nonce)
2173 GNUNET_free (nonce);
2174 GNUNET_SCHEDULER_add_now (&do_error, handle);
2175 return;
2176 }
2177 id_token = OIDC_generate_id_token (&ticket.audience,
2178 &ticket.identity,
2179 cl,
2180 pl,
2181 &expiration_time,
2182 (NULL != nonce) ? nonce : NULL,
2183 jwt_secret);
2184 GNUNET_free (jwt_secret);
2185 if (NULL != nonce)
2186 GNUNET_free (nonce);
2187 access_token = OIDC_access_token_new (&ticket);
2188 /* Store mapping from access token to code so we can later
2189 * fall back on the provided attributes in userinfo
2190 */
2191 GNUNET_CRYPTO_hash (access_token,
2192 strlen (access_token),
2193 &cache_key);
2194 char *tmp_at = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2195 &cache_key);
2196 GNUNET_CONTAINER_multihashmap_put (oidc_code_cache,
2197 &cache_key,
2198 code,
2199 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
2200 /* If there was a previous code in there, free the old value */
2201 if (NULL != tmp_at)
2202 {
2203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2204 "OIDC access token already issued. Cleanup.\n");
2205 GNUNET_free (tmp_at);
2206 }
2207
2208 OIDC_build_token_response (access_token,
2209 id_token,
2210 &expiration_time,
2211 &json_response);
2212
2213 resp = GNUNET_REST_create_response (json_response);
2214 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2215 "Cache-Control",
2216 "no-store"));
2217 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2218 "Pragma", "no-cache"));
2219 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2220 "Content-Type",
2221 "application/json"));
2222 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2223 GNUNET_RECLAIM_attribute_list_destroy (cl);
2224 GNUNET_RECLAIM_presentation_list_destroy (pl);
2225 GNUNET_free (access_token);
2226 GNUNET_free (json_response);
2227 GNUNET_free (id_token);
2228 cleanup_handle (handle);
2229}
2230
2231
2232/**
2233 * Collects claims and stores them in handle
2234 */
2235static void
2236consume_ticket (void *cls,
2237 const struct GNUNET_IDENTITY_PublicKey *identity,
2238 const struct GNUNET_RECLAIM_Attribute *attr,
2239 const struct GNUNET_RECLAIM_Presentation *presentation)
2240{
2241 struct RequestHandle *handle = cls;
2242 struct GNUNET_RECLAIM_AttributeListEntry *ale;
2243 struct GNUNET_RECLAIM_PresentationListEntry *atle;
2244 struct MHD_Response *resp;
2245 char *result_str;
2246
2247 if (NULL != handle->consume_timeout_op)
2248 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
2249 handle->consume_timeout_op = NULL;
2250 handle->idp_op = NULL;
2251
2252 if (NULL == identity)
2253 {
2254 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2255 handle->attr_userinfo_list,
2256 handle->presentations);
2257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2258 resp = GNUNET_REST_create_response (result_str);
2259 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2260 GNUNET_free (result_str);
2261 cleanup_handle (handle);
2262 return;
2263 }
2264 ale = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
2265 ale->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
2266 &attr->credential,
2267 attr->type,
2268 attr->data,
2269 attr->data_size);
2270 ale->attribute->id = attr->id;
2271 ale->attribute->flag = attr->flag;
2272 ale->attribute->credential = attr->credential;
2273 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
2274 handle->attr_userinfo_list->list_tail,
2275 ale);
2276 if (NULL == presentation)
2277 return;
2278 for (atle = handle->presentations->list_head;
2279 NULL != atle; atle = atle->next)
2280 {
2281 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (
2282 &atle->presentation->credential_id,
2283 &presentation->credential_id))
2284 continue;
2285 break; /** already in list **/
2286 }
2287 if (NULL == atle)
2288 {
2289 /** Credential matches for attribute, add **/
2290 atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry);
2291 atle->presentation = GNUNET_RECLAIM_presentation_new (presentation->type,
2292 presentation->data,
2293 presentation->
2294 data_size);
2295 atle->presentation->credential_id = presentation->credential_id;
2296 GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head,
2297 handle->presentations->list_tail,
2298 atle);
2299 }
2300}
2301
2302
2303static void
2304consume_timeout (void*cls)
2305{
2306 struct RequestHandle *handle = cls;
2307 struct GNUNET_HashCode cache_key;
2308 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2309 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2310 struct GNUNET_RECLAIM_Ticket ticket;
2311 char *nonce;
2312 char *cached_code;
2313
2314 handle->consume_timeout_op = NULL;
2315 if (NULL != handle->idp_op)
2316 GNUNET_RECLAIM_cancel (handle->idp_op);
2317 handle->idp_op = NULL;
2318
2319 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2320 "Ticket consumptioned timed out. Using cache...\n");
2321 GNUNET_CRYPTO_hash (handle->access_token,
2322 strlen (handle->access_token),
2323 &cache_key);
2324 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2325 &cache_key);
2326 if (NULL == cached_code)
2327 {
2328 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2329 handle->edesc = GNUNET_strdup ("No Access Token in cache!");
2330 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2331 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2332 return;
2333 }
2334
2335 // decode code
2336 if (GNUNET_OK != OIDC_parse_authz_code (&handle->ticket.audience,
2337 cached_code, NULL, &ticket,
2338 &cl, &pl, &nonce,
2339 OIDC_VERIFICATION_NO_CODE_VERIFIER))
2340 {
2341 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2342 handle->edesc = GNUNET_strdup ("invalid code");
2343 handle->response_code = MHD_HTTP_BAD_REQUEST;
2344 GNUNET_free (cached_code);
2345 if (NULL != nonce)
2346 GNUNET_free (nonce);
2347 GNUNET_SCHEDULER_add_now (&do_error, handle);
2348 return;
2349 }
2350
2351 struct MHD_Response *resp;
2352 char *result_str;
2353
2354 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2355 cl,
2356 pl);
2357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2358 resp = GNUNET_REST_create_response (result_str);
2359 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2360 GNUNET_free (result_str);
2361 GNUNET_free (nonce);
2362 GNUNET_RECLAIM_attribute_list_destroy (cl);
2363 GNUNET_RECLAIM_presentation_list_destroy (pl);
2364 cleanup_handle (handle);
2365}
2366
2367
2368/**
2369 * Responds to userinfo GET and url-encoded POST request
2370 *
2371 * @param con_handle the connection handle
2372 * @param url the url
2373 * @param cls the RequestHandle
2374 */
2375static void
2376userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2377 const char *url,
2378 void *cls)
2379{
2380 // TODO expiration time
2381 struct RequestHandle *handle = cls;
2382 struct GNUNET_RECLAIM_Ticket *ticket;
2383 char delimiter[] = " ";
2384 struct GNUNET_HashCode cache_key;
2385 char *authorization;
2386 char *authorization_type;
2387 char *authorization_access_token;
2388 const struct EgoEntry *aud_ego;
2389 const struct GNUNET_IDENTITY_PrivateKey *privkey;
2390
2391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting userinfo\n");
2392 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2393 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2394 &cache_key);
2395 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2396 ->header_param_map,
2397 &cache_key))
2398 {
2399 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2400 handle->edesc = GNUNET_strdup ("No Access Token");
2401 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2402 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2403 return;
2404 }
2405 authorization =
2406 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2407 &cache_key);
2408
2409 // split header in "Bearer" and access_token
2410 authorization = GNUNET_strdup (authorization);
2411 authorization_type = strtok (authorization, delimiter);
2412 if ((NULL == authorization_type) ||
2413 (0 != strcmp ("Bearer", authorization_type)))
2414 {
2415 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2416 handle->edesc = GNUNET_strdup ("No Access Token");
2417 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2418 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2419 GNUNET_free (authorization);
2420 return;
2421 }
2422 authorization_access_token = strtok (NULL, delimiter);
2423 if (NULL == authorization_access_token)
2424 {
2425 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2426 handle->edesc = GNUNET_strdup ("Access token missing");
2427 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2428 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2429 GNUNET_free (authorization);
2430 return;
2431 }
2432
2433 if (GNUNET_OK != OIDC_access_token_parse (authorization_access_token,
2434 &ticket))
2435 {
2436 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2437 handle->edesc = GNUNET_strdup ("The access token is invalid");
2438 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2439 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2440 GNUNET_free (authorization);
2441 return;
2442
2443 }
2444 GNUNET_assert (NULL != ticket);
2445 handle->ticket = *ticket;
2446 GNUNET_free (ticket);
2447 aud_ego = find_ego (handle, &handle->ticket.audience);
2448 if (NULL == aud_ego)
2449 {
2450 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2451 handle->edesc = GNUNET_strdup ("The access token expired");
2452 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2453 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2454 GNUNET_free (authorization);
2455 return;
2456 }
2457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Consuming ticket\n");
2458 privkey = GNUNET_IDENTITY_ego_get_private_key (aud_ego->ego);
2459 handle->attr_userinfo_list =
2460 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
2461 handle->presentations =
2462 GNUNET_new (struct GNUNET_RECLAIM_PresentationList);
2463
2464 /* If the consume takes too long, we use values from the cache */
2465 handle->access_token = GNUNET_strdup (authorization_access_token);
2466 handle->consume_timeout_op = GNUNET_SCHEDULER_add_delayed (CONSUME_TIMEOUT,
2467 &consume_timeout,
2468 handle);
2469 handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
2470 privkey,
2471 &handle->ticket,
2472 &consume_ticket,
2473 handle);
2474 GNUNET_free (authorization);
2475}
2476
2477
2478/**
2479 * If listing is enabled, prints information about the egos.
2480 *
2481 * This function is initially called for all egos and then again
2482 * whenever a ego's identifier changes or if it is deleted. At the
2483 * end of the initial pass over all egos, the function is once called
2484 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2485 * be invoked in the future or that there was an error.
2486 *
2487 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
2488 * function is only called ONCE, and 'NULL' being passed in 'ego' does
2489 * indicate an error (for example because name is taken or no default value is
2490 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
2491 * value WILL be passed to a subsequent call to the identity callback of
2492 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
2493 *
2494 * When an identity is renamed, this function is called with the
2495 * (known) ego but the NEW identifier.
2496 *
2497 * When an identity is deleted, this function is called with the
2498 * (known) ego and "NULL" for the 'identifier'. In this case,
2499 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2500 * cleaned up).
2501 *
2502 * @param cls closure
2503 * @param ego ego handle
2504 * @param ctx context for application to store data for this ego
2505 * (during the lifetime of this process, initially NULL)
2506 * @param identifier identifier assigned by the user for this ego,
2507 * NULL if the user just deleted the ego and it
2508 * must thus no longer be used
2509 */
2510static void
2511list_ego (void *cls,
2512 struct GNUNET_IDENTITY_Ego *ego,
2513 void **ctx,
2514 const char *identifier)
2515{
2516 struct EgoEntry *ego_entry;
2517 struct GNUNET_IDENTITY_PublicKey pk;
2518
2519 if (NULL == ego)
2520 {
2521 state = ID_REST_STATE_POST_INIT;
2522 return;
2523 }
2524 if (ID_REST_STATE_INIT == state)
2525
2526 {
2527 ego_entry = GNUNET_new (struct EgoEntry);
2528 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2529 ego_entry->keystring = GNUNET_IDENTITY_public_key_to_string (&pk);
2530 ego_entry->ego = ego;
2531 ego_entry->identifier = GNUNET_strdup (identifier);
2532 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2533 ego_tail,
2534 ego_entry);
2535 return;
2536 }
2537 /* Ego renamed or added */
2538 if (identifier != NULL)
2539 {
2540 for (ego_entry = ego_head; NULL != ego_entry;
2541 ego_entry = ego_entry->next)
2542 {
2543 if (ego_entry->ego == ego)
2544 {
2545 /* Rename */
2546 GNUNET_free (ego_entry->identifier);
2547 ego_entry->identifier = GNUNET_strdup (identifier);
2548 break;
2549 }
2550 }
2551 if (NULL == ego_entry)
2552 {
2553 /* Add */
2554 ego_entry = GNUNET_new (struct EgoEntry);
2555 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2556 ego_entry->keystring = GNUNET_IDENTITY_public_key_to_string (&pk);
2557 ego_entry->ego = ego;
2558 ego_entry->identifier = GNUNET_strdup (identifier);
2559 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2560 ego_tail,
2561 ego_entry);
2562 }
2563 }
2564 else
2565 {
2566 /* Delete */
2567 for (ego_entry = ego_head; NULL != ego_entry;
2568 ego_entry = ego_entry->next)
2569 {
2570 if (ego_entry->ego == ego)
2571 break;
2572 }
2573 if (NULL == ego_entry)
2574 return; /* Not found */
2575
2576 GNUNET_CONTAINER_DLL_remove (ego_head,
2577 ego_tail,
2578 ego_entry);
2579 GNUNET_free (ego_entry->identifier);
2580 GNUNET_free (ego_entry->keystring);
2581 GNUNET_free (ego_entry);
2582 return;
2583 }
2584}
2585
2586
2587static void
2588oidc_config_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2589 const char *url,
2590 void *cls)
2591{
2592 json_t *oidc_config;
2593 json_t *auth_methods;
2594 json_t *sig_algs;
2595 json_t *scopes;
2596 json_t *response_types;
2597 json_t *sub_types;
2598 json_t *claim_types;
2599 char *oidc_config_str;
2600 struct MHD_Response *resp;
2601 struct RequestHandle *handle = cls;
2602
2603 oidc_config = json_object ();
2604 // FIXME get from config?
2605 json_object_set_new (oidc_config,
2606 "issuer", json_string ("http://localhost:7776"));
2607 json_object_set_new (oidc_config,
2608 "authorization_endpoint",
2609 json_string ("https://api.reclaim/openid/authorize"));
2610 json_object_set_new (oidc_config,
2611 "token_endpoint",
2612 json_string ("http://localhost:7776/openid/token"));
2613 auth_methods = json_array ();
2614 json_array_append_new (auth_methods,
2615 json_string ("client_secret_basic"));
2616 json_array_append_new (auth_methods,
2617 json_string ("client_secret_post"));
2618 json_object_set_new (oidc_config,
2619 "token_endpoint_auth_methods_supported",
2620 auth_methods);
2621 sig_algs = json_array ();
2622 json_array_append_new (sig_algs,
2623 json_string ("HS512"));
2624 json_object_set_new (oidc_config,
2625 "id_token_signing_alg_values_supported",
2626 sig_algs);
2627 json_object_set_new (oidc_config,
2628 "userinfo_endpoint",
2629 json_string ("http://localhost:7776/openid/userinfo"));
2630 scopes = json_array ();
2631 json_array_append_new (scopes,
2632 json_string ("openid"));
2633 json_array_append_new (scopes,
2634 json_string ("profile"));
2635 json_array_append_new (scopes,
2636 json_string ("email"));
2637 json_array_append_new (scopes,
2638 json_string ("address"));
2639 json_array_append_new (scopes,
2640 json_string ("phone"));
2641 json_object_set_new (oidc_config,
2642 "scopes_supported",
2643 scopes);
2644 response_types = json_array ();
2645 json_array_append_new (response_types,
2646 json_string ("code"));
2647 json_object_set_new (oidc_config,
2648 "response_types_supported",
2649 response_types);
2650 sub_types = json_array ();
2651 json_array_append_new (sub_types,
2652 json_string ("public")); /* no pairwise support */
2653 json_object_set_new (oidc_config,
2654 "subject_types_supported",
2655 sub_types);
2656 claim_types = json_array ();
2657 json_array_append_new (claim_types,
2658 json_string ("normal"));
2659 json_array_append_new (claim_types,
2660 json_string ("aggregated"));
2661 json_object_set_new (oidc_config,
2662 "claim_types_supported",
2663 claim_types);
2664 json_object_set_new (oidc_config,
2665 "claims_parameter_supported",
2666 json_boolean (1));
2667 oidc_config_str = json_dumps (oidc_config, JSON_INDENT (1));
2668 resp = GNUNET_REST_create_response (oidc_config_str);
2669 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2670 json_decref (oidc_config);
2671 GNUNET_free (oidc_config_str);
2672 cleanup_handle (handle);
2673}
2674
2675
2676/**
2677 * Respond to OPTIONS request
2678 *
2679 * @param con_handle the connection handle
2680 * @param url the url
2681 * @param cls the RequestHandle
2682 */
2683static void
2684oidc_config_cors (struct GNUNET_REST_RequestHandle *con_handle,
2685 const char *url,
2686 void *cls)
2687{
2688 struct MHD_Response *resp;
2689 struct RequestHandle *handle = cls;
2690
2691 // For now, independent of path return all options
2692 resp = GNUNET_REST_create_response (NULL);
2693 GNUNET_assert (MHD_NO !=
2694 MHD_add_response_header (resp,
2695 "Access-Control-Allow-Methods",
2696 allow_methods));
2697 GNUNET_assert (MHD_NO !=
2698 MHD_add_response_header (resp,
2699 "Access-Control-Allow-Origin",
2700 "*"));
2701 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2702 cleanup_handle (handle);
2703 return;
2704}
2705
2706
2707static enum GNUNET_GenericReturnValue
2708rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2709 GNUNET_REST_ResultProcessor proc,
2710 void *proc_cls)
2711{
2712 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2713 struct GNUNET_REST_RequestHandlerError err;
2714 static const struct GNUNET_REST_RequestHandler handlers[] =
2715 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
2716 { MHD_HTTP_METHOD_POST,
2717 GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint }, // url-encoded
2718 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
2719 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2720 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2721 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2722 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG,
2723 &oidc_config_endpoint },
2724 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG,
2725 &oidc_config_cors },
2726 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
2727 GNUNET_REST_HANDLER_END };
2728
2729 handle->oidc = GNUNET_new (struct OIDC_Variables);
2730 if (NULL == OIDC_cookie_jar_map)
2731 OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10,
2732 GNUNET_NO);
2733 if (NULL == oidc_code_cache)
2734 oidc_code_cache = GNUNET_CONTAINER_multihashmap_create (10,
2735 GNUNET_NO);
2736
2737 handle->response_code = 0;
2738 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2739 handle->proc_cls = proc_cls;
2740 handle->proc = proc;
2741 handle->rest_handle = rest_handle;
2742 handle->url = GNUNET_strdup (rest_handle->url);
2743 handle->timeout_task =
2744 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
2745 GNUNET_CONTAINER_DLL_insert (requests_head,
2746 requests_tail,
2747 handle);
2748 if (handle->url[strlen (handle->url) - 1] == '/')
2749 handle->url[strlen (handle->url) - 1] = '\0';
2750 if (GNUNET_NO ==
2751 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
2752 return GNUNET_NO;
2753
2754 return GNUNET_YES;
2755}
2756
2757
2758/**
2759 * Entry point for the plugin.
2760 *
2761 * @param cls Config info
2762 * @return NULL on error, otherwise the plugin context
2763 */
2764void *
2765libgnunet_plugin_rest_openid_connect_init (void *cls)
2766{
2767 static struct Plugin plugin;
2768 struct GNUNET_REST_Plugin *api;
2769
2770 cfg = cls;
2771 if (NULL != plugin.cfg)
2772 return NULL; /* can only initialize once! */
2773 memset (&plugin, 0, sizeof(struct Plugin));
2774 plugin.cfg = cfg;
2775 api = GNUNET_new (struct GNUNET_REST_Plugin);
2776 api->cls = &plugin;
2777 api->name = GNUNET_REST_API_NS_OIDC;
2778 api->process_request = &rest_identity_process_request;
2779 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
2780 gns_handle = GNUNET_GNS_connect (cfg);
2781 idp = GNUNET_RECLAIM_connect (cfg);
2782
2783 state = ID_REST_STATE_INIT;
2784 GNUNET_asprintf (&allow_methods,
2785 "%s, %s, %s, %s, %s",
2786 MHD_HTTP_METHOD_GET,
2787 MHD_HTTP_METHOD_POST,
2788 MHD_HTTP_METHOD_PUT,
2789 MHD_HTTP_METHOD_DELETE,
2790 MHD_HTTP_METHOD_OPTIONS);
2791
2792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2793 _ ("OpenID Connect REST API initialized\n"));
2794 return api;
2795}
2796
2797
2798static int
2799cleanup_hashmap (void *cls, const struct GNUNET_HashCode *key, void *value)
2800{
2801 GNUNET_free (value);
2802 return GNUNET_YES;
2803}
2804
2805
2806/**
2807 * Exit point from the plugin.
2808 *
2809 * @param cls the plugin context (as returned by "init")
2810 * @return always NULL
2811 */
2812void *
2813libgnunet_plugin_rest_openid_connect_done (void *cls)
2814{
2815 struct GNUNET_REST_Plugin *api = cls;
2816 struct Plugin *plugin = api->cls;
2817 struct EgoEntry *ego_entry;
2818
2819 plugin->cfg = NULL;
2820 while (NULL != requests_head)
2821 cleanup_handle (requests_head);
2822 if (NULL != OIDC_cookie_jar_map)
2823 {
2824 GNUNET_CONTAINER_multihashmap_iterate (OIDC_cookie_jar_map,
2825 &cleanup_hashmap,
2826 NULL);
2827 GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2828 }
2829 if (NULL != oidc_code_cache)
2830 {
2831 GNUNET_CONTAINER_multihashmap_iterate (oidc_code_cache,
2832 &cleanup_hashmap,
2833 NULL);
2834 GNUNET_CONTAINER_multihashmap_destroy (oidc_code_cache);
2835 }
2836
2837 GNUNET_free (allow_methods);
2838 if (NULL != gns_handle)
2839 GNUNET_GNS_disconnect (gns_handle);
2840 if (NULL != identity_handle)
2841 GNUNET_IDENTITY_disconnect (identity_handle);
2842 if (NULL != idp)
2843 GNUNET_RECLAIM_disconnect (idp);
2844 while (NULL != (ego_entry = ego_head))
2845 {
2846 GNUNET_CONTAINER_DLL_remove (ego_head,
2847 ego_tail,
2848 ego_entry);
2849 GNUNET_free (ego_entry->identifier);
2850 GNUNET_free (ego_entry->keystring);
2851 GNUNET_free (ego_entry);
2852 }
2853 GNUNET_free (api);
2854 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2855 "OpenID Connect REST plugin is finished\n");
2856 return NULL;
2857}
2858
2859
2860/* end of plugin_rest_openid_connect.c */