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