memcmp() -> GNUNET_memcmp(), first take
[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_attribute_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 cookie expiration (in seconds)
124  */
125 #define OIDC_COOKIE_EXPIRATION 3
126
127 /**
128  * OIDC cookie header key
129  */
130 #define OIDC_COOKIE_HEADER_KEY "cookie"
131
132 /**
133  * OIDC cookie header information key
134  */
135 #define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
136
137 /**
138  * OIDC cookie header information key
139  */
140 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
141
142 /**
143  * OIDC cookie header if user cancelled
144  */
145 #define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
146
147 /**
148  * OIDC expected response_type while authorizing
149  */
150 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
151
152 /**
153  * OIDC expected scope part while authorizing
154  */
155 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
156
157 /**
158  * OIDC error key for invalid client
159  */
160 #define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
161
162 /**
163  * OIDC error key for invalid scopes
164  */
165 #define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
166
167 /**
168  * OIDC error key for invalid requests
169  */
170 #define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
171
172 /**
173  * OIDC error key for invalid tokens
174  */
175 #define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
176
177 /**
178  * OIDC error key for invalid cookies
179  */
180 #define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
181
182 /**
183  * OIDC error key for generic server errors
184  */
185 #define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
186
187 /**
188  * OIDC error key for unsupported grants
189  */
190 #define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
191
192 /**
193  * OIDC error key for unsupported response types
194  */
195 #define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
196
197 /**
198  * OIDC error key for unauthorized clients
199  */
200 #define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
201
202 /**
203  * OIDC error key for denied access
204  */
205 #define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
206
207
208 /**
209  * OIDC ignored parameter array
210  */
211 static char *OIDC_ignored_parameter_array[] = {
212     "display",       "prompt",     "ui_locales", "response_mode",
213     "id_token_hint", "login_hint", "acr_values"};
214
215 /**
216  * OIDC Hash map that keeps track of issued cookies
217  */
218 struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
219
220 /**
221  * OIDC authorized identities and times hashmap
222  */
223 struct GNUNET_CONTAINER_MultiHashMap *OIDC_identity_grants;
224
225 /**
226  * OIDC Hash map that keeps track of used authorization code(s)
227  */
228 struct GNUNET_CONTAINER_MultiHashMap *OIDC_used_ticket_map;
229
230 /**
231  * Hash map that links the issued access token to the corresponding ticket and
232  * ego
233  */
234 struct GNUNET_CONTAINER_MultiHashMap *OIDC_access_token_map;
235
236 /**
237  * The configuration handle
238  */
239 const struct GNUNET_CONFIGURATION_Handle *cfg;
240
241 /**
242  * HTTP methods allows for this plugin
243  */
244 static char *allow_methods;
245
246 /**
247  * @brief struct returned by the initialization function of the plugin
248  */
249 struct Plugin
250 {
251   const struct GNUNET_CONFIGURATION_Handle *cfg;
252 };
253
254 /**
255  * OIDC needed variables
256  */
257 struct OIDC_Variables
258 {
259   /**
260    * The RP client public key
261    */
262   struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
263
264   /**
265    * The OIDC client id of the RP
266    */
267   char *client_id;
268
269   /**
270    * The OIDC redirect uri
271    */
272   char *redirect_uri;
273
274   /**
275    * The list of oidc scopes
276    */
277   char *scope;
278
279   /**
280    * The OIDC state
281    */
282   char *state;
283
284   /**
285    * The OIDC nonce
286    */
287   char *nonce;
288
289   /**
290    * The OIDC response type
291    */
292   char *response_type;
293
294   /**
295    * The identity chosen by the user to login
296    */
297   char *login_identity;
298
299   /**
300    * User cancelled authorization/login
301    */
302   int user_cancelled;
303
304   /**
305    * The response JSON
306    */
307   json_t *response;
308 };
309
310 /**
311  * The ego list
312  */
313 struct EgoEntry
314 {
315   /**
316    * DLL
317    */
318   struct EgoEntry *next;
319
320   /**
321    * DLL
322    */
323   struct EgoEntry *prev;
324
325   /**
326    * Ego Identifier
327    */
328   char *identifier;
329
330   /**
331    * Public key string
332    */
333   char *keystring;
334
335   /**
336    * The Ego
337    */
338   struct GNUNET_IDENTITY_Ego *ego;
339 };
340
341
342 struct RequestHandle
343 {
344   /**
345    * Ego list
346    */
347   struct EgoEntry *ego_head;
348
349   /**
350    * Ego list
351    */
352   struct EgoEntry *ego_tail;
353
354   /**
355    * Selected ego
356    */
357   struct EgoEntry *ego_entry;
358
359   /**
360    * Pointer to ego private key
361    */
362   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
363
364   /**
365    * OIDC variables
366    */
367   struct OIDC_Variables *oidc;
368
369   /**
370    * The processing state
371    */
372   int state;
373
374   /**
375    * Handle to Identity service.
376    */
377   struct GNUNET_IDENTITY_Handle *identity_handle;
378
379   /**
380    * Rest connection
381    */
382   struct GNUNET_REST_RequestHandle *rest_handle;
383
384   /**
385    * GNS handle
386    */
387   struct GNUNET_GNS_Handle *gns_handle;
388
389   /**
390    * GNS lookup op
391    */
392   struct GNUNET_GNS_LookupRequest *gns_op;
393
394   /**
395    * Handle to NAMESTORE
396    */
397   struct GNUNET_NAMESTORE_Handle *namestore_handle;
398
399   /**
400    * Iterator for NAMESTORE
401    */
402   struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
403
404   /**
405    * Attribute claim list
406    */
407   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
408
409   /**
410    * IDENTITY Operation
411    */
412   struct GNUNET_IDENTITY_Operation *op;
413
414   /**
415    * Identity Provider
416    */
417   struct GNUNET_RECLAIM_Handle *idp;
418
419   /**
420    * Idp Operation
421    */
422   struct GNUNET_RECLAIM_Operation *idp_op;
423
424   /**
425    * Attribute iterator
426    */
427   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
428
429   /**
430    * Ticket iterator
431    */
432   struct GNUNET_RECLAIM_TicketIterator *ticket_it;
433
434   /**
435    * A ticket
436    */
437   struct GNUNET_RECLAIM_Ticket ticket;
438
439   /**
440    * Desired timeout for the lookup (default is no timeout).
441    */
442   struct GNUNET_TIME_Relative timeout;
443
444   /**
445    * ID of a task associated with the resolution process.
446    */
447   struct GNUNET_SCHEDULER_Task *timeout_task;
448
449   /**
450    * The plugin result processor
451    */
452   GNUNET_REST_ResultProcessor proc;
453
454   /**
455    * The closure of the result processor
456    */
457   void *proc_cls;
458
459   /**
460    * The url
461    */
462   char *url;
463
464   /**
465    * The tld for redirect
466    */
467   char *tld;
468
469   /**
470    * The redirect prefix
471    */
472   char *redirect_prefix;
473
474   /**
475    * The redirect suffix
476    */
477   char *redirect_suffix;
478
479   /**
480    * Error response message
481    */
482   char *emsg;
483
484   /**
485    * Error response description
486    */
487   char *edesc;
488
489   /**
490    * Reponse code
491    */
492   int response_code;
493 };
494
495 /**
496  * Cleanup lookup handle
497  * @param handle Handle to clean up
498  */
499 static void
500 cleanup_handle (struct RequestHandle *handle)
501 {
502   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
503   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
504   struct EgoEntry *ego_entry;
505   struct EgoEntry *ego_tmp;
506   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
507   if (NULL != handle->timeout_task)
508     GNUNET_SCHEDULER_cancel (handle->timeout_task);
509   if (NULL != handle->identity_handle)
510     GNUNET_IDENTITY_disconnect (handle->identity_handle);
511   if (NULL != handle->attr_it)
512     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
513   if (NULL != handle->ticket_it)
514     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
515   if (NULL != handle->idp)
516     GNUNET_RECLAIM_disconnect (handle->idp);
517   GNUNET_free_non_null (handle->url);
518   GNUNET_free_non_null (handle->tld);
519   GNUNET_free_non_null (handle->redirect_prefix);
520   GNUNET_free_non_null (handle->redirect_suffix);
521   GNUNET_free_non_null (handle->emsg);
522   GNUNET_free_non_null (handle->edesc);
523   if (NULL != handle->gns_op)
524     GNUNET_GNS_lookup_cancel (handle->gns_op);
525   if (NULL != handle->gns_handle)
526     GNUNET_GNS_disconnect (handle->gns_handle);
527
528   if (NULL != handle->namestore_handle)
529     GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
530   if (NULL != handle->oidc) {
531     GNUNET_free_non_null (handle->oidc->client_id);
532     GNUNET_free_non_null (handle->oidc->login_identity);
533     GNUNET_free_non_null (handle->oidc->nonce);
534     GNUNET_free_non_null (handle->oidc->redirect_uri);
535     GNUNET_free_non_null (handle->oidc->response_type);
536     GNUNET_free_non_null (handle->oidc->scope);
537     GNUNET_free_non_null (handle->oidc->state);
538     json_decref (handle->oidc->response);
539     GNUNET_free (handle->oidc);
540   }
541   if (NULL != handle->attr_list) {
542     for (claim_entry = handle->attr_list->list_head; NULL != claim_entry;) {
543       claim_tmp = claim_entry;
544       claim_entry = claim_entry->next;
545       GNUNET_free (claim_tmp->claim);
546       GNUNET_free (claim_tmp);
547     }
548     GNUNET_free (handle->attr_list);
549   }
550   for (ego_entry = handle->ego_head; NULL != ego_entry;) {
551     ego_tmp = ego_entry;
552     ego_entry = ego_entry->next;
553     GNUNET_free (ego_tmp->identifier);
554     GNUNET_free (ego_tmp->keystring);
555     GNUNET_free (ego_tmp);
556   }
557   GNUNET_free_non_null (handle->attr_it);
558   GNUNET_free (handle);
559 }
560
561 static void
562 cleanup_handle_delayed (void *cls)
563 {
564   cleanup_handle (cls);
565 }
566
567
568 /**
569  * Task run on error, sends error message.  Cleans up everything.
570  *
571  * @param cls the `struct RequestHandle`
572  */
573 static void
574 do_error (void *cls)
575 {
576   struct RequestHandle *handle = cls;
577   struct MHD_Response *resp;
578   char *json_error;
579
580   GNUNET_asprintf (
581       &json_error,
582       "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
583       handle->emsg, (NULL != handle->edesc) ? handle->edesc : "",
584       (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
585       (NULL != handle->oidc->state) ? handle->oidc->state : "",
586       (NULL != handle->oidc->state) ? "\"" : "");
587   if (0 == handle->response_code)
588     handle->response_code = MHD_HTTP_BAD_REQUEST;
589   resp = GNUNET_REST_create_response (json_error);
590   if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
591     MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Basic");
592   MHD_add_response_header (resp, MHD_HTTP_HEADER_CONTENT_TYPE,
593                            "application/json");
594   handle->proc (handle->proc_cls, resp, handle->response_code);
595   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
596   GNUNET_free (json_error);
597 }
598
599
600 /**
601  * Task run on error in userinfo endpoint, sends error header. Cleans up
602  * everything
603  *
604  * @param cls the `struct RequestHandle`
605  */
606 static void
607 do_userinfo_error (void *cls)
608 {
609   struct RequestHandle *handle = cls;
610   struct MHD_Response *resp;
611   char *error;
612
613   GNUNET_asprintf (&error, "error=\"%s\", error_description=\"%s\"",
614                    handle->emsg, (NULL != handle->edesc) ? handle->edesc : "");
615   resp = GNUNET_REST_create_response ("");
616   MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Bearer");
617   handle->proc (handle->proc_cls, resp, handle->response_code);
618   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
619   GNUNET_free (error);
620 }
621
622
623 /**
624  * Task run on error, sends error message and redirects. Cleans up everything.
625  *
626  * @param cls the `struct RequestHandle`
627  */
628 static void
629 do_redirect_error (void *cls)
630 {
631   struct RequestHandle *handle = cls;
632   struct MHD_Response *resp;
633   char *redirect;
634   GNUNET_asprintf (&redirect, "%s?error=%s&error_description=%s%s%s",
635                    handle->oidc->redirect_uri, handle->emsg, handle->edesc,
636                    (NULL != handle->oidc->state) ? "&state=" : "",
637                    (NULL != handle->oidc->state) ? handle->oidc->state : "");
638   resp = GNUNET_REST_create_response ("");
639   MHD_add_response_header (resp, "Location", redirect);
640   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
641   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
642   GNUNET_free (redirect);
643 }
644
645 /**
646  * Task run on timeout, sends error message.  Cleans up everything.
647  *
648  * @param cls the `struct RequestHandle`
649  */
650 static void
651 do_timeout (void *cls)
652 {
653   struct RequestHandle *handle = cls;
654
655   handle->timeout_task = NULL;
656   do_error (handle);
657 }
658
659 /**
660  * Return attributes for claim
661  *
662  * @param cls the request handle
663  */
664 static void
665 return_userinfo_response (void *cls)
666 {
667   char *result_str;
668   struct RequestHandle *handle = cls;
669   struct MHD_Response *resp;
670
671   result_str = json_dumps (handle->oidc->response, 0);
672
673   resp = GNUNET_REST_create_response (result_str);
674   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
675   GNUNET_free (result_str);
676   cleanup_handle (handle);
677 }
678
679 /**
680  * Returns base64 encoded string urlencoded
681  *
682  * @param string the string to encode
683  * @return base64 encoded string
684  */
685 static char *
686 base64_encode (const char *s)
687 {
688   char *enc;
689   char *enc_urlencode;
690   char *tmp;
691   int i;
692   int num_pads = 0;
693
694   GNUNET_STRINGS_base64_encode (s, strlen (s), &enc);
695   tmp = strchr (enc, '=');
696   num_pads = strlen (enc) - (tmp - enc);
697   GNUNET_assert ((3 > num_pads) && (0 <= num_pads));
698   if (0 == num_pads)
699     return enc;
700   enc_urlencode = GNUNET_malloc (strlen (enc) + num_pads * 2);
701   strcpy (enc_urlencode, enc);
702   GNUNET_free (enc);
703   tmp = strchr (enc_urlencode, '=');
704   for (i = 0; i < num_pads; i++) {
705     strcpy (tmp, "%3D"); // replace '=' with '%3D'
706     tmp += 3;
707   }
708   return enc_urlencode;
709 }
710
711 /**
712  * Respond to OPTIONS request
713  *
714  * @param con_handle the connection handle
715  * @param url the url
716  * @param cls the RequestHandle
717  */
718 static void
719 options_cont (struct GNUNET_REST_RequestHandle *con_handle, const char *url,
720               void *cls)
721 {
722   struct MHD_Response *resp;
723   struct RequestHandle *handle = cls;
724
725   // For now, independent of path return all options
726   resp = GNUNET_REST_create_response (NULL);
727   MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
728   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
729   cleanup_handle (handle);
730   return;
731 }
732
733
734 /**
735  * Interprets cookie header and pass its identity keystring to handle
736  */
737 static void
738 cookie_identity_interpretation (struct RequestHandle *handle)
739 {
740   struct GNUNET_HashCode cache_key;
741   char *cookies;
742   struct GNUNET_TIME_Absolute current_time, *relog_time;
743   char delimiter[] = "; ";
744   char *tmp_cookies;
745   char *token;
746   char *value;
747
748   // gets identity of login try with cookie
749   GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY, strlen (OIDC_COOKIE_HEADER_KEY),
750                       &cache_key);
751   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
752                        handle->rest_handle->header_param_map, &cache_key)) {
753     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
754     return;
755   }
756   // splits cookies and find 'Identity' cookie
757   tmp_cookies = GNUNET_CONTAINER_multihashmap_get (
758       handle->rest_handle->header_param_map, &cache_key);
759   cookies = GNUNET_strdup (tmp_cookies);
760   token = strtok (cookies, delimiter);
761   handle->oidc->user_cancelled = GNUNET_NO;
762   handle->oidc->login_identity = NULL;
763   if (NULL == token) {
764     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse cookie: %s\n",
765                 cookies);
766     GNUNET_free (cookies);
767     return;
768   }
769
770   while (NULL != token) {
771     if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED)) {
772       handle->oidc->user_cancelled = GNUNET_YES;
773       GNUNET_free (cookies);
774       return;
775     }
776     if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
777       break;
778     token = strtok (NULL, delimiter);
779   }
780   if (NULL == token) {
781     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No cookie value to process: %s\n",
782                 cookies);
783     GNUNET_free (cookies);
784     return;
785   }
786   GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
787   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map,
788                                                            &cache_key)) {
789     GNUNET_log (
790         GNUNET_ERROR_TYPE_WARNING,
791         "Found cookie `%s', but no corresponding expiration entry present...\n",
792         token);
793     GNUNET_free (cookies);
794     return;
795   }
796   relog_time =
797       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
798   current_time = GNUNET_TIME_absolute_get ();
799   // 30 min after old login -> redirect to login
800   if (current_time.abs_value_us > relog_time->abs_value_us) {
801     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
802                 "Found cookie `%s', but it is expired.\n", token);
803     GNUNET_free (cookies);
804     return;
805   }
806   value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
807   GNUNET_assert (NULL != value);
808   handle->oidc->login_identity = GNUNET_strdup (value);
809 }
810
811 /**
812  * Redirects to login page stored in configuration file
813  */
814 static void
815 login_redirect (void *cls)
816 {
817   char *login_base_url;
818   char *new_redirect;
819   struct MHD_Response *resp;
820   struct RequestHandle *handle = cls;
821
822   if (GNUNET_OK ==
823       GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
824                                              "address", &login_base_url)) {
825     GNUNET_asprintf (&new_redirect, "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
826                      login_base_url, OIDC_RESPONSE_TYPE_KEY,
827                      handle->oidc->response_type, OIDC_CLIENT_ID_KEY,
828                      handle->oidc->client_id, OIDC_REDIRECT_URI_KEY,
829                      handle->oidc->redirect_uri, OIDC_SCOPE_KEY,
830                      handle->oidc->scope, OIDC_STATE_KEY,
831                      (NULL != handle->oidc->state) ? handle->oidc->state : "",
832                      OIDC_NONCE_KEY,
833                      (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
834     resp = GNUNET_REST_create_response ("");
835     MHD_add_response_header (resp, "Location", new_redirect);
836     GNUNET_free (login_base_url);
837   } else {
838     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
839     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
840     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
841     GNUNET_SCHEDULER_add_now (&do_error, handle);
842     return;
843   }
844   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
845   GNUNET_free (new_redirect);
846   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
847 }
848
849 /**
850  * Does internal server error when iteration failed.
851  */
852 static void
853 oidc_iteration_error (void *cls)
854 {
855   struct RequestHandle *handle = cls;
856   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
857   handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
858   GNUNET_SCHEDULER_add_now (&do_error, handle);
859 }
860
861
862 /**
863  * Issues ticket and redirects to relying party with the authorization code as
864  * parameter. Otherwise redirects with error
865  */
866 static void
867 oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
868 {
869   struct RequestHandle *handle = cls;
870   struct MHD_Response *resp;
871   char *ticket_str;
872   char *redirect_uri;
873   char *code_json_string;
874   char *code_base64_final_string;
875
876   handle->idp_op = NULL;
877   handle->ticket = *ticket;
878   if (NULL == ticket) {
879     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
880     handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
881     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
882     return;
883   }
884   ticket_str = GNUNET_STRINGS_data_to_string_alloc (
885       &handle->ticket, sizeof (struct GNUNET_RECLAIM_Ticket));
886   // TODO change if more attributes are needed (see max_age)
887   code_json_string = OIDC_build_authz_code (&handle->priv_key, &handle->ticket,
888                                             handle->oidc->nonce);
889   code_base64_final_string = base64_encode (code_json_string);
890   if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
891       (NULL != handle->tld)) {
892
893     GNUNET_asprintf (&redirect_uri, "%s.%s/%s?%s=%s&state=%s",
894                      handle->redirect_prefix, handle->tld,
895                      handle->redirect_suffix, handle->oidc->response_type,
896                      code_base64_final_string, handle->oidc->state);
897   } else {
898     GNUNET_asprintf (&redirect_uri, "%s?%s=%s&state=%s",
899                      handle->oidc->redirect_uri, handle->oidc->response_type,
900                      code_base64_final_string, handle->oidc->state);
901   }
902   resp = GNUNET_REST_create_response ("");
903   MHD_add_response_header (resp, "Location", redirect_uri);
904   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
905   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
906   GNUNET_free (redirect_uri);
907   GNUNET_free (ticket_str);
908   GNUNET_free (code_json_string);
909   GNUNET_free (code_base64_final_string);
910 }
911
912 static void
913 oidc_collect_finished_cb (void *cls)
914 {
915   struct RequestHandle *handle = cls;
916   handle->attr_it = NULL;
917   handle->ticket_it = NULL;
918   if (NULL == handle->attr_list->list_head) {
919     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
920     handle->edesc = GNUNET_strdup ("The requested scope is not available.");
921     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
922     return;
923   }
924   handle->idp_op = GNUNET_RECLAIM_ticket_issue (
925       handle->idp, &handle->priv_key, &handle->oidc->client_pkey,
926       handle->attr_list, &oidc_ticket_issue_cb, handle);
927 }
928
929
930 /**
931  * Collects all attributes for an ego if in scope parameter
932  */
933 static void
934 oidc_attr_collect (void *cls,
935                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
936                    const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
937 {
938   struct RequestHandle *handle = cls;
939   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
940   char *scope_variables;
941   char *scope_variable;
942   char delimiter[] = " ";
943
944   if ((NULL == attr->name) || (NULL == attr->data)) {
945     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
946     return;
947   }
948
949   scope_variables = GNUNET_strdup (handle->oidc->scope);
950   scope_variable = strtok (scope_variables, delimiter);
951   while (NULL != scope_variable) {
952     if (0 == strcmp (attr->name, scope_variable)) {
953       break;
954     }
955     scope_variable = strtok (NULL, delimiter);
956   }
957   if (NULL == scope_variable) {
958     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
959     GNUNET_free (scope_variables);
960     return;
961   }
962   GNUNET_free (scope_variables);
963
964   le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
965   le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type,
966                                                   attr->data, attr->data_size);
967   GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
968                                handle->attr_list->list_tail, le);
969   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
970 }
971
972
973 /**
974  * Checks time and cookie and redirects accordingly
975  */
976 static void
977 code_redirect (void *cls)
978 {
979   struct RequestHandle *handle = cls;
980   struct GNUNET_TIME_Absolute current_time, *relog_time;
981   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
982   struct GNUNET_HashCode cache_key;
983   char *identity_cookie;
984
985   GNUNET_asprintf (&identity_cookie, "Identity=%s",
986                    handle->oidc->login_identity);
987   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
988   GNUNET_free (identity_cookie);
989   // No login time for identity -> redirect to login
990   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map,
991                                                             &cache_key)) {
992     relog_time =
993         GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
994     current_time = GNUNET_TIME_absolute_get ();
995     // 30 min after old login -> redirect to login
996     if (current_time.abs_value_us <= relog_time->abs_value_us) {
997       if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (
998                            handle->oidc->login_identity,
999                            strlen (handle->oidc->login_identity), &pubkey)) {
1000         handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1001         handle->edesc =
1002             GNUNET_strdup ("The cookie of a login identity is not valid");
1003         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1004         return;
1005       }
1006       // iterate over egos and compare their public key
1007       for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1008            handle->ego_entry = handle->ego_entry->next) {
1009         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1010         if (0 == GNUNET_memcmp (&ego_pkey, &pubkey)) {
1011           handle->priv_key =
1012               *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1013           handle->idp = GNUNET_RECLAIM_connect (cfg);
1014           handle->attr_list =
1015               GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1016           handle->attr_it = GNUNET_RECLAIM_get_attributes_start (
1017               handle->idp, &handle->priv_key, &oidc_iteration_error, handle,
1018               &oidc_attr_collect, handle, &oidc_collect_finished_cb, handle);
1019           return;
1020         }
1021       }
1022       GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1023       return;
1024     }
1025   }
1026 }
1027
1028
1029 static void
1030 build_redirect (void *cls)
1031 {
1032   struct RequestHandle *handle = cls;
1033   struct MHD_Response *resp;
1034   char *redirect_uri;
1035
1036   if (GNUNET_YES == handle->oidc->user_cancelled) {
1037     if ((NULL != handle->redirect_prefix) &&
1038         (NULL != handle->redirect_suffix) && (NULL != handle->tld)) {
1039       GNUNET_asprintf (
1040           &redirect_uri, "%s.%s/%s?error=%s&error_description=%s&state=%s",
1041           handle->redirect_prefix, handle->tld, handle->redirect_suffix,
1042           "access_denied", "User denied access", handle->oidc->state);
1043     } else {
1044       GNUNET_asprintf (&redirect_uri,
1045                        "%s?error=%s&error_description=%s&state=%s",
1046                        handle->oidc->redirect_uri, "access_denied",
1047                        "User denied access", handle->oidc->state);
1048     }
1049     resp = GNUNET_REST_create_response ("");
1050     MHD_add_response_header (resp, "Location", redirect_uri);
1051     handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1052     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1053     GNUNET_free (redirect_uri);
1054     return;
1055   }
1056   GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1057 }
1058
1059
1060 static void
1061 lookup_redirect_uri_result (void *cls, uint32_t rd_count,
1062                             const struct GNUNET_GNSRECORD_Data *rd)
1063 {
1064   struct RequestHandle *handle = cls;
1065   char *tmp;
1066   char *tmp_key_str;
1067   char *pos;
1068   struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1069
1070   handle->gns_op = NULL;
1071   if (0 == rd_count) {
1072     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1073     handle->edesc = GNUNET_strdup (
1074         "Server cannot generate ticket, redirect uri not found.");
1075     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1076     return;
1077   }
1078   for (int i = 0; i < rd_count; i++) {
1079     if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1080       continue;
1081     if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1082       continue;
1083     tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1084     if (NULL == strstr (tmp, handle->oidc->client_id)) {
1085       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1086                   "Redirect uri %s does not contain client_id %s", tmp,
1087                   handle->oidc->client_id);
1088     } else {
1089
1090       pos = strrchr (tmp, (unsigned char)'.');
1091       *pos = '\0';
1092       handle->redirect_prefix = GNUNET_strdup (tmp);
1093       tmp_key_str = pos + 1;
1094       pos = strchr (tmp_key_str, (unsigned char)'/');
1095       *pos = '\0';
1096       handle->redirect_suffix = GNUNET_strdup (pos + 1);
1097
1098       GNUNET_STRINGS_string_to_data (tmp_key_str, strlen (tmp_key_str),
1099                                      &redirect_zone, sizeof (redirect_zone));
1100     }
1101     GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1102     GNUNET_free (tmp);
1103     return;
1104   }
1105   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1106   handle->edesc =
1107       GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1108   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1109 }
1110
1111
1112 /**
1113  * Initiate redirect back to client.
1114  */
1115 static void
1116 client_redirect (void *cls)
1117 {
1118   struct RequestHandle *handle = cls;
1119
1120   /* Lookup client redirect uri to verify request */
1121   handle->gns_op = GNUNET_GNS_lookup (
1122       handle->gns_handle, GNUNET_GNS_EMPTY_LABEL_AT, &handle->oidc->client_pkey,
1123       GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT, GNUNET_GNS_LO_DEFAULT,
1124       &lookup_redirect_uri_result, handle);
1125 }
1126
1127
1128 /**
1129  * Iteration over all results finished, build final
1130  * response.
1131  *
1132  * @param cls the `struct RequestHandle`
1133  */
1134 static void
1135 build_authz_response (void *cls)
1136 {
1137   struct RequestHandle *handle = cls;
1138   struct GNUNET_HashCode cache_key;
1139
1140   char *expected_scope;
1141   char delimiter[] = " ";
1142   int number_of_ignored_parameter, iterator;
1143
1144
1145   // REQUIRED value: redirect_uri
1146   GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1147                       &cache_key);
1148   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1149                        handle->rest_handle->url_param_map, &cache_key)) {
1150     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1151     handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1152     GNUNET_SCHEDULER_add_now (&do_error, handle);
1153     return;
1154   }
1155   handle->oidc->redirect_uri =
1156       GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (
1157           handle->rest_handle->url_param_map, &cache_key));
1158
1159   // REQUIRED value: response_type
1160   GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1161                       &cache_key);
1162   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1163                        handle->rest_handle->url_param_map, &cache_key)) {
1164     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1165     handle->edesc = GNUNET_strdup ("missing parameter response_type");
1166     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1167     return;
1168   }
1169   handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get (
1170       handle->rest_handle->url_param_map, &cache_key);
1171   handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1172
1173   // REQUIRED value: scope
1174   GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1175   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1176                        handle->rest_handle->url_param_map, &cache_key)) {
1177     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1178     handle->edesc = GNUNET_strdup ("missing parameter scope");
1179     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1180     return;
1181   }
1182   handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get (
1183       handle->rest_handle->url_param_map, &cache_key);
1184   handle->oidc->scope = GNUNET_strdup (handle->oidc->scope);
1185
1186   // OPTIONAL value: nonce
1187   GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1188   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (
1189                         handle->rest_handle->url_param_map, &cache_key)) {
1190     handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get (
1191         handle->rest_handle->url_param_map, &cache_key);
1192     handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1193   }
1194
1195   // TODO check other values if needed
1196   number_of_ignored_parameter =
1197       sizeof (OIDC_ignored_parameter_array) / sizeof (char *);
1198   for (iterator = 0; iterator < number_of_ignored_parameter; iterator++) {
1199     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1200                         strlen (OIDC_ignored_parameter_array[iterator]),
1201                         &cache_key);
1202     if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (
1203                           handle->rest_handle->url_param_map, &cache_key)) {
1204       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1205       GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1206                        OIDC_ignored_parameter_array[iterator]);
1207       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1208       return;
1209     }
1210   }
1211
1212   // Checks if response_type is 'code'
1213   if (0 != strcmp (handle->oidc->response_type,
1214                    OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE)) {
1215     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1216     handle->edesc = GNUNET_strdup ("The authorization server does not support "
1217                                    "obtaining this authorization code.");
1218     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1219     return;
1220   }
1221
1222   // Checks if scope contains 'openid'
1223   expected_scope = GNUNET_strdup (handle->oidc->scope);
1224   char *test;
1225   test = strtok (expected_scope, delimiter);
1226   while (NULL != test) {
1227     if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1228       break;
1229     test = strtok (NULL, delimiter);
1230   }
1231   if (NULL == test) {
1232     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1233     handle->edesc =
1234         GNUNET_strdup ("The requested scope is invalid, unknown, or "
1235                        "malformed.");
1236     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1237     GNUNET_free (expected_scope);
1238     return;
1239   }
1240
1241   GNUNET_free (expected_scope);
1242   if ((NULL == handle->oidc->login_identity) &&
1243       (GNUNET_NO == handle->oidc->user_cancelled))
1244     GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1245   else
1246     GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1247 }
1248
1249 /**
1250  * Iterate over tlds in config
1251  */
1252 static void
1253 tld_iter (void *cls, const char *section, const char *option, const char *value)
1254 {
1255   struct RequestHandle *handle = cls;
1256   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1257
1258   if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1259                        value, strlen (value), &pkey)) {
1260     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1261     return;
1262   }
1263   if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1264     handle->tld = GNUNET_strdup (option + 1);
1265 }
1266
1267 /**
1268  * Responds to authorization GET and url-encoded POST request
1269  *
1270  * @param con_handle the connection handle
1271  * @param url the url
1272  * @param cls the RequestHandle
1273  */
1274 static void
1275 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1276                     const char *url, void *cls)
1277 {
1278   struct RequestHandle *handle = cls;
1279   struct GNUNET_HashCode cache_key;
1280   struct EgoEntry *tmp_ego;
1281   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1282   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1283
1284   cookie_identity_interpretation (handle);
1285
1286   // RECOMMENDED value: state - REQUIRED for answers
1287   GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1288   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (
1289                         handle->rest_handle->url_param_map, &cache_key)) {
1290     handle->oidc->state = GNUNET_CONTAINER_multihashmap_get (
1291         handle->rest_handle->url_param_map, &cache_key);
1292     handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1293   }
1294
1295   // REQUIRED value: client_id
1296   GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1297                       &cache_key);
1298   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1299                        handle->rest_handle->url_param_map, &cache_key)) {
1300     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1301     handle->edesc = GNUNET_strdup ("missing parameter client_id");
1302     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1303     GNUNET_SCHEDULER_add_now (&do_error, handle);
1304     return;
1305   }
1306   handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get (
1307       handle->rest_handle->url_param_map, &cache_key));
1308
1309   if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1310                        handle->oidc->client_id,
1311                        strlen (handle->oidc->client_id),
1312                        &handle->oidc->client_pkey)) {
1313     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1314     handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1315                                    "authorization code using this method.");
1316     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1317     GNUNET_SCHEDULER_add_now (&do_error, handle);
1318     return;
1319   }
1320
1321
1322   if (NULL == handle->ego_head) {
1323     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1324     handle->edesc = GNUNET_strdup ("Egos are missing");
1325     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1326     GNUNET_SCHEDULER_add_now (&do_error, handle);
1327     return;
1328   }
1329
1330   handle->ego_entry = handle->ego_head;
1331   handle->priv_key =
1332       *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1333   // If we know this identity, translated the corresponding TLD
1334   // TODO: We might want to have a reverse lookup functionality for TLDs?
1335   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next) {
1336     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1337     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, &pkey);
1338     if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey)) {
1339       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1340       handle->ego_entry = handle->ego_tail;
1341     }
1342   }
1343   if (NULL == handle->tld)
1344     GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1345   if (NULL == handle->tld)
1346     handle->tld = GNUNET_strdup (handle->oidc->client_id);
1347   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1348 }
1349
1350 /**
1351  * Combines an identity with a login time and responds OK to login request
1352  *
1353  * @param con_handle the connection handle
1354  * @param url the url
1355  * @param cls the RequestHandle
1356  */
1357 static void
1358 login_cont (struct GNUNET_REST_RequestHandle *con_handle, const char *url,
1359             void *cls)
1360 {
1361   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1362   struct RequestHandle *handle = cls;
1363   struct GNUNET_HashCode cache_key;
1364   struct GNUNET_TIME_Absolute *current_time;
1365   struct GNUNET_TIME_Absolute *last_time;
1366   char *cookie;
1367   char *header_val;
1368   json_t *root;
1369   json_error_t error;
1370   json_t *identity;
1371   char term_data[handle->rest_handle->data_size + 1];
1372   term_data[handle->rest_handle->data_size] = '\0';
1373   GNUNET_memcpy (term_data, handle->rest_handle->data,
1374                  handle->rest_handle->data_size);
1375   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1376   identity = json_object_get (root, "identity");
1377   if (!json_is_string (identity)) {
1378     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing json string from %s\n",
1379                 term_data);
1380     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1381     json_decref (root);
1382     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1383     return;
1384   }
1385   GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1386   GNUNET_asprintf (&header_val, "%s;Max-Age=%d", cookie,
1387                    OIDC_COOKIE_EXPIRATION);
1388   MHD_add_response_header (resp, "Set-Cookie", header_val);
1389   MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1390   GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1391
1392   if (0 != strcmp (json_string_value (identity), "Denied")) {
1393     current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1394     *current_time =
1395         GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
1396             GNUNET_TIME_relative_get_second_ (), OIDC_COOKIE_EXPIRATION));
1397     last_time =
1398         GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1399     GNUNET_free_non_null (last_time);
1400     GNUNET_CONTAINER_multihashmap_put (
1401         OIDC_cookie_jar_map, &cache_key, current_time,
1402         GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1403   }
1404   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1405   GNUNET_free (cookie);
1406   GNUNET_free (header_val);
1407   json_decref (root);
1408   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1409 }
1410
1411 static int
1412 check_authorization (struct RequestHandle *handle,
1413                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1414 {
1415   struct GNUNET_HashCode cache_key;
1416   char *authorization;
1417   char *credentials;
1418   char *basic_authorization;
1419   char *client_id;
1420   char *pass;
1421   char *expected_pass;
1422   int client_exists = GNUNET_NO;
1423
1424   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1425                       strlen (OIDC_AUTHORIZATION_HEADER_KEY), &cache_key);
1426   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1427                        handle->rest_handle->header_param_map, &cache_key)) {
1428     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1429     handle->edesc = GNUNET_strdup ("missing authorization");
1430     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1431     return GNUNET_SYSERR;
1432   }
1433   authorization = GNUNET_CONTAINER_multihashmap_get (
1434       handle->rest_handle->header_param_map, &cache_key);
1435
1436   // split header in "Basic" and [content]
1437   credentials = strtok (authorization, " ");
1438   if (0 != strcmp ("Basic", credentials)) {
1439     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1440     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1441     return GNUNET_SYSERR;
1442   }
1443   credentials = strtok (NULL, " ");
1444   if (NULL == credentials) {
1445     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1446     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1447     return GNUNET_SYSERR;
1448   }
1449   GNUNET_STRINGS_base64_decode (credentials, strlen (credentials),
1450                                 (void **)&basic_authorization);
1451
1452   if (NULL == basic_authorization) {
1453     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1454     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1455     return GNUNET_SYSERR;
1456   }
1457   client_id = strtok (basic_authorization, ":");
1458   if (NULL == client_id) {
1459     GNUNET_free_non_null (basic_authorization);
1460     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1461     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1462     return GNUNET_SYSERR;
1463   }
1464   pass = strtok (NULL, ":");
1465   if (NULL == pass) {
1466     GNUNET_free_non_null (basic_authorization);
1467     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1468     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1469     return GNUNET_SYSERR;
1470   }
1471
1472   // check client password
1473   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (
1474                        cfg, "reclaim-rest-plugin", "psw", &expected_pass)) {
1475     if (0 != strcmp (expected_pass, pass)) {
1476       GNUNET_free_non_null (basic_authorization);
1477       GNUNET_free (expected_pass);
1478       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1479       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1480       return GNUNET_SYSERR;
1481     }
1482     GNUNET_free (expected_pass);
1483   } else {
1484     GNUNET_free_non_null (basic_authorization);
1485     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1486     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1487     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1488     return GNUNET_SYSERR;
1489   }
1490
1491   // check client_id
1492   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;) {
1493     if (0 == strcmp (handle->ego_entry->keystring, client_id)) {
1494       client_exists = GNUNET_YES;
1495       break;
1496     }
1497     handle->ego_entry = handle->ego_entry->next;
1498   }
1499   if (GNUNET_NO == client_exists) {
1500     GNUNET_free_non_null (basic_authorization);
1501     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1502     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1503     return GNUNET_SYSERR;
1504   }
1505   GNUNET_STRINGS_string_to_data (client_id, strlen (client_id), cid,
1506                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1507
1508   GNUNET_free (basic_authorization);
1509   return GNUNET_OK;
1510 }
1511
1512 static int
1513 ego_exists (struct RequestHandle *handle,
1514             struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1515 {
1516   struct EgoEntry *ego_entry;
1517   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1518
1519   for (ego_entry = handle->ego_head; NULL != ego_entry;
1520        ego_entry = ego_entry->next) {
1521     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1522     if (0 == GNUNET_memcmp (&pub_key, test_key)) {
1523       break;
1524     }
1525   }
1526   if (NULL == ego_entry)
1527     return GNUNET_NO;
1528   return GNUNET_YES;
1529 }
1530
1531 static void
1532 store_ticket_reference (const struct RequestHandle *handle,
1533                         const char *access_token,
1534                         const struct GNUNET_RECLAIM_Ticket *ticket,
1535                         const struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1536 {
1537   struct GNUNET_HashCode cache_key;
1538   char *id_ticket_combination;
1539   char *ticket_string;
1540   char *client_id;
1541
1542   GNUNET_CRYPTO_hash (access_token, strlen (access_token), &cache_key);
1543   client_id = GNUNET_STRINGS_data_to_string_alloc (
1544       cid, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1545   ticket_string = GNUNET_STRINGS_data_to_string_alloc (
1546       ticket, sizeof (struct GNUNET_RECLAIM_Ticket));
1547   GNUNET_asprintf (&id_ticket_combination, "%s;%s", client_id, ticket_string);
1548   GNUNET_CONTAINER_multihashmap_put (
1549       OIDC_access_token_map, &cache_key, id_ticket_combination,
1550       GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1551
1552   GNUNET_free (client_id);
1553   GNUNET_free (ticket_string);
1554 }
1555
1556 /**
1557  * Responds to token url-encoded POST request
1558  *
1559  * @param con_handle the connection handle
1560  * @param url the url
1561  * @param cls the RequestHandle
1562  */
1563 static void
1564 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, const char *url,
1565                 void *cls)
1566 {
1567   struct RequestHandle *handle = cls;
1568   struct GNUNET_TIME_Relative expiration_time;
1569   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl;
1570   struct GNUNET_RECLAIM_Ticket *ticket;
1571   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1572   struct GNUNET_HashCode cache_key;
1573   struct MHD_Response *resp;
1574   char *grant_type;
1575   char *code;
1576   char *json_response;
1577   char *id_token;
1578   char *access_token;
1579   char *jwt_secret;
1580   char *nonce;
1581   int i = 1;
1582
1583   /*
1584    * Check Authorization
1585    */
1586   if (GNUNET_SYSERR == check_authorization (handle, &cid)) {
1587     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1588                 "OIDC authorization for token endpoint failed\n");
1589     GNUNET_SCHEDULER_add_now (&do_error, handle);
1590     return;
1591   }
1592
1593   /*
1594    * Check parameter
1595    */
1596
1597   // TODO Do not allow multiple equal parameter names
1598   // REQUIRED grant_type
1599   GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY),
1600                       &cache_key);
1601   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1602                        handle->rest_handle->url_param_map, &cache_key)) {
1603     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1604     handle->edesc = GNUNET_strdup ("missing parameter grant_type");
1605     handle->response_code = MHD_HTTP_BAD_REQUEST;
1606     GNUNET_SCHEDULER_add_now (&do_error, handle);
1607     return;
1608   }
1609   grant_type = GNUNET_CONTAINER_multihashmap_get (
1610       handle->rest_handle->url_param_map, &cache_key);
1611
1612   // REQUIRED code
1613   GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
1614   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1615                        handle->rest_handle->url_param_map, &cache_key)) {
1616     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1617     handle->edesc = GNUNET_strdup ("missing parameter code");
1618     handle->response_code = MHD_HTTP_BAD_REQUEST;
1619     GNUNET_SCHEDULER_add_now (&do_error, handle);
1620     return;
1621   }
1622   code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1623                                             &cache_key);
1624
1625   // REQUIRED redirect_uri
1626   GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1627                       &cache_key);
1628   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1629                        handle->rest_handle->url_param_map, &cache_key)) {
1630     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1631     handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1632     handle->response_code = MHD_HTTP_BAD_REQUEST;
1633     GNUNET_SCHEDULER_add_now (&do_error, handle);
1634     return;
1635   }
1636
1637   // Check parameter grant_type == "authorization_code"
1638   if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type)) {
1639     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
1640     handle->response_code = MHD_HTTP_BAD_REQUEST;
1641     GNUNET_SCHEDULER_add_now (&do_error, handle);
1642     return;
1643   }
1644   GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1645   if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (
1646                            OIDC_used_ticket_map, &cache_key, &i,
1647                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) {
1648     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1649     handle->edesc = GNUNET_strdup ("Cannot use the same code more than once");
1650     handle->response_code = MHD_HTTP_BAD_REQUEST;
1651     GNUNET_SCHEDULER_add_now (&do_error, handle);
1652     return;
1653   }
1654
1655   // decode code
1656   if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, &ticket, &nonce)) {
1657     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1658     handle->edesc = GNUNET_strdup ("invalid code");
1659     handle->response_code = MHD_HTTP_BAD_REQUEST;
1660     GNUNET_SCHEDULER_add_now (&do_error, handle);
1661     return;
1662   }
1663
1664   // create jwt
1665   if (GNUNET_OK !=
1666       GNUNET_CONFIGURATION_get_value_time (
1667           cfg, "reclaim-rest-plugin", "expiration_time", &expiration_time)) {
1668     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1669     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1670     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1671     GNUNET_SCHEDULER_add_now (&do_error, handle);
1672     GNUNET_free (ticket);
1673     return;
1674   }
1675
1676
1677   // TODO OPTIONAL acr,amr,azp
1678   if (GNUNET_NO == ego_exists (handle, &ticket->audience)) {
1679     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1680     handle->edesc = GNUNET_strdup ("invalid code...");
1681     handle->response_code = MHD_HTTP_BAD_REQUEST;
1682     GNUNET_SCHEDULER_add_now (&do_error, handle);
1683     GNUNET_free (ticket);
1684   }
1685   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
1686                        cfg, "reclaim-rest-plugin", "jwt_secret", &jwt_secret)) {
1687     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1688     handle->edesc = GNUNET_strdup ("No signing secret configured!");
1689     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1690     GNUNET_SCHEDULER_add_now (&do_error, handle);
1691     GNUNET_free (ticket);
1692     return;
1693   }
1694   // TODO We should collect the attributes here. cl always empty
1695   cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1696   id_token = OIDC_id_token_new (&ticket->audience, &ticket->identity, cl,
1697                                 &expiration_time,
1698                                 (NULL != nonce) ? nonce : NULL, jwt_secret);
1699   access_token = OIDC_access_token_new ();
1700   OIDC_build_token_response (access_token, id_token, &expiration_time,
1701                              &json_response);
1702
1703   store_ticket_reference (handle, access_token, ticket, &cid);
1704   resp = GNUNET_REST_create_response (json_response);
1705   MHD_add_response_header (resp, "Cache-Control", "no-store");
1706   MHD_add_response_header (resp, "Pragma", "no-cache");
1707   MHD_add_response_header (resp, "Content-Type", "application/json");
1708   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1709   GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl);
1710   GNUNET_free (access_token);
1711   GNUNET_free (json_response);
1712   GNUNET_free (ticket);
1713   GNUNET_free (id_token);
1714   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1715 }
1716
1717 /**
1718  * Collects claims and stores them in handle
1719  */
1720 static void
1721 consume_ticket (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1722                 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1723 {
1724   struct RequestHandle *handle = cls;
1725   char *tmp_value;
1726   json_t *value;
1727
1728   if (NULL == identity) {
1729     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1730     return;
1731   }
1732   tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, attr->data,
1733                                                         attr->data_size);
1734   value = json_string (tmp_value);
1735   json_object_set_new (handle->oidc->response, attr->name, value);
1736   GNUNET_free (tmp_value);
1737 }
1738
1739 /**
1740  * Responds to userinfo GET and url-encoded POST request
1741  *
1742  * @param con_handle the connection handle
1743  * @param url the url
1744  * @param cls the RequestHandle
1745  */
1746 static void
1747 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1748                    const char *url, void *cls)
1749 {
1750   // TODO expiration time
1751   struct RequestHandle *handle = cls;
1752   char delimiter[] = " ";
1753   char delimiter_db[] = ";";
1754   struct GNUNET_HashCode cache_key;
1755   char *authorization, *authorization_type, *authorization_access_token;
1756   char *client_ticket, *client, *ticket_str;
1757   struct GNUNET_RECLAIM_Ticket *ticket;
1758
1759   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1760                       strlen (OIDC_AUTHORIZATION_HEADER_KEY), &cache_key);
1761   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1762                        handle->rest_handle->header_param_map, &cache_key)) {
1763     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1764     handle->edesc = GNUNET_strdup ("No Access Token");
1765     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1766     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1767     return;
1768   }
1769   authorization = GNUNET_CONTAINER_multihashmap_get (
1770       handle->rest_handle->header_param_map, &cache_key);
1771
1772   // split header in "Bearer" and access_token
1773   authorization = GNUNET_strdup (authorization);
1774   authorization_type = strtok (authorization, delimiter);
1775   if (0 != strcmp ("Bearer", authorization_type)) {
1776     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1777     handle->edesc = GNUNET_strdup ("No Access Token");
1778     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1779     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1780     GNUNET_free (authorization);
1781     return;
1782   }
1783   authorization_access_token = strtok (NULL, delimiter);
1784   if (NULL == authorization_access_token) {
1785     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1786     handle->edesc = GNUNET_strdup ("No Access Token");
1787     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1788     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1789     GNUNET_free (authorization);
1790     return;
1791   }
1792
1793   GNUNET_CRYPTO_hash (authorization_access_token,
1794                       strlen (authorization_access_token), &cache_key);
1795   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1796                        OIDC_access_token_map, &cache_key)) {
1797     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1798     handle->edesc = GNUNET_strdup ("The Access Token expired");
1799     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1800     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1801     GNUNET_free (authorization);
1802     return;
1803   }
1804
1805   client_ticket =
1806       GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, &cache_key);
1807   client_ticket = GNUNET_strdup (client_ticket);
1808   client = strtok (client_ticket, delimiter_db);
1809   if (NULL == client) {
1810     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1811     handle->edesc = GNUNET_strdup ("The Access Token expired");
1812     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1813     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1814     GNUNET_free (authorization);
1815     GNUNET_free (client_ticket);
1816     return;
1817   }
1818   handle->ego_entry = handle->ego_head;
1819   for (; NULL != handle->ego_entry;
1820        handle->ego_entry = handle->ego_entry->next) {
1821     if (0 == strcmp (handle->ego_entry->keystring, client))
1822       break;
1823   }
1824   if (NULL == handle->ego_entry) {
1825     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1826     handle->edesc = GNUNET_strdup ("The Access Token expired");
1827     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1828     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1829     GNUNET_free (authorization);
1830     GNUNET_free (client_ticket);
1831     return;
1832   }
1833   ticket_str = strtok (NULL, delimiter_db);
1834   if (NULL == ticket_str) {
1835     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1836     handle->edesc = GNUNET_strdup ("The Access Token expired");
1837     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1838     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1839     GNUNET_free (authorization);
1840     GNUNET_free (client_ticket);
1841     return;
1842   }
1843   ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
1844   if (GNUNET_OK !=
1845       GNUNET_STRINGS_string_to_data (ticket_str, strlen (ticket_str), ticket,
1846                                      sizeof (struct GNUNET_RECLAIM_Ticket))) {
1847     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1848     handle->edesc = GNUNET_strdup ("The Access Token expired");
1849     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1850     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1851     GNUNET_free (ticket);
1852     GNUNET_free (authorization);
1853     GNUNET_free (client_ticket);
1854     return;
1855   }
1856
1857   handle->idp = GNUNET_RECLAIM_connect (cfg);
1858   handle->oidc->response = json_object ();
1859   json_object_set_new (handle->oidc->response, "sub",
1860                        json_string (handle->ego_entry->keystring));
1861   handle->idp_op = GNUNET_RECLAIM_ticket_consume (
1862       handle->idp, GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
1863       ticket, consume_ticket, handle);
1864   GNUNET_free (ticket);
1865   GNUNET_free (authorization);
1866   GNUNET_free (client_ticket);
1867 }
1868
1869
1870 /**
1871  * Handle rest request
1872  *
1873  * @param handle the request handle
1874  */
1875 static void
1876 init_cont (struct RequestHandle *handle)
1877 {
1878   struct GNUNET_REST_RequestHandlerError err;
1879   static const struct GNUNET_REST_RequestHandler handlers[] = {
1880       {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
1881       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE,
1882        &authorize_endpoint}, // url-encoded
1883       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1884       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint},
1885       {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint},
1886       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint},
1887       {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont},
1888       GNUNET_REST_HANDLER_END};
1889
1890   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, handlers,
1891                                                &err, handle)) {
1892     handle->response_code = err.error_code;
1893     GNUNET_SCHEDULER_add_now (&do_error, handle);
1894   }
1895 }
1896
1897 /**
1898  * If listing is enabled, prints information about the egos.
1899  *
1900  * This function is initially called for all egos and then again
1901  * whenever a ego's identifier changes or if it is deleted.  At the
1902  * end of the initial pass over all egos, the function is once called
1903  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1904  * be invoked in the future or that there was an error.
1905  *
1906  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1907  * this function is only called ONCE, and 'NULL' being passed in
1908  * 'ego' does indicate an error (i.e. name is taken or no default
1909  * value is known).  If 'ego' is non-NULL and if '*ctx'
1910  * is set in those callbacks, the value WILL be passed to a subsequent
1911  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1912  * that one was not NULL).
1913  *
1914  * When an identity is renamed, this function is called with the
1915  * (known) ego but the NEW identifier.
1916  *
1917  * When an identity is deleted, this function is called with the
1918  * (known) ego and "NULL" for the 'identifier'.  In this case,
1919  * the 'ego' is henceforth invalid (and the 'ctx' should also be
1920  * cleaned up).
1921  *
1922  * @param cls closure
1923  * @param ego ego handle
1924  * @param ctx context for application to store data for this ego
1925  *                 (during the lifetime of this process, initially NULL)
1926  * @param identifier identifier assigned by the user for this ego,
1927  *                   NULL if the user just deleted the ego and it
1928  *                   must thus no longer be used
1929  */
1930 static void
1931 list_ego (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx,
1932           const char *identifier)
1933 {
1934   struct RequestHandle *handle = cls;
1935   struct EgoEntry *ego_entry;
1936   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1937
1938   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) {
1939     handle->state = ID_REST_STATE_POST_INIT;
1940     init_cont (handle);
1941     return;
1942   }
1943   if (ID_REST_STATE_INIT == handle->state) {
1944     ego_entry = GNUNET_new (struct EgoEntry);
1945     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1946     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1947     ego_entry->ego = ego;
1948     ego_entry->identifier = GNUNET_strdup (identifier);
1949     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, handle->ego_tail,
1950                                       ego_entry);
1951     return;
1952   }
1953   /* Ego renamed or added */
1954   if (identifier != NULL) {
1955     for (ego_entry = handle->ego_head; NULL != ego_entry;
1956          ego_entry = ego_entry->next) {
1957       if (ego_entry->ego == ego) {
1958         /* Rename */
1959         GNUNET_free (ego_entry->identifier);
1960         ego_entry->identifier = GNUNET_strdup (identifier);
1961         break;
1962       }
1963     }
1964     if (NULL == ego_entry) {
1965       /* Add */
1966       ego_entry = GNUNET_new (struct EgoEntry);
1967       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1968       ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1969       ego_entry->ego = ego;
1970       ego_entry->identifier = GNUNET_strdup (identifier);
1971       GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, handle->ego_tail,
1972                                         ego_entry);
1973     }
1974   } else {
1975     /* Delete */
1976     for (ego_entry = handle->ego_head; NULL != ego_entry;
1977          ego_entry = ego_entry->next) {
1978       if (ego_entry->ego == ego)
1979         break;
1980     }
1981     if (NULL != ego_entry)
1982       GNUNET_CONTAINER_DLL_remove (handle->ego_head, handle->ego_tail,
1983                                    ego_entry);
1984   }
1985 }
1986
1987 static void
1988 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
1989                                GNUNET_REST_ResultProcessor proc, void *proc_cls)
1990 {
1991   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1992   handle->oidc = GNUNET_new (struct OIDC_Variables);
1993   if (NULL == OIDC_cookie_jar_map)
1994     OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
1995   if (NULL == OIDC_identity_grants)
1996     OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
1997   if (NULL == OIDC_used_ticket_map)
1998     OIDC_used_ticket_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
1999   if (NULL == OIDC_access_token_map)
2000     OIDC_access_token_map =
2001         GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2002   handle->response_code = 0;
2003   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2004   handle->proc_cls = proc_cls;
2005   handle->proc = proc;
2006   handle->state = ID_REST_STATE_INIT;
2007   handle->rest_handle = rest_handle;
2008
2009   handle->url = GNUNET_strdup (rest_handle->url);
2010   if (handle->url[strlen (handle->url) - 1] == '/')
2011     handle->url[strlen (handle->url) - 1] = '\0';
2012   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
2013   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, handle);
2014   handle->gns_handle = GNUNET_GNS_connect (cfg);
2015   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2016   handle->timeout_task =
2017       GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
2018   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
2019 }
2020
2021 /**
2022  * Entry point for the plugin.
2023  *
2024  * @param cls Config info
2025  * @return NULL on error, otherwise the plugin context
2026  */
2027 void *
2028 libgnunet_plugin_rest_openid_connect_init (void *cls)
2029 {
2030   static struct Plugin plugin;
2031   struct GNUNET_REST_Plugin *api;
2032
2033   cfg = cls;
2034   if (NULL != plugin.cfg)
2035     return NULL; /* can only initialize once! */
2036   memset (&plugin, 0, sizeof (struct Plugin));
2037   plugin.cfg = cfg;
2038   api = GNUNET_new (struct GNUNET_REST_Plugin);
2039   api->cls = &plugin;
2040   api->name = GNUNET_REST_API_NS_OIDC;
2041   api->process_request = &rest_identity_process_request;
2042   GNUNET_asprintf (&allow_methods, "%s, %s, %s, %s, %s", MHD_HTTP_METHOD_GET,
2043                    MHD_HTTP_METHOD_POST, MHD_HTTP_METHOD_PUT,
2044                    MHD_HTTP_METHOD_DELETE, MHD_HTTP_METHOD_OPTIONS);
2045
2046   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2047               _ ("Identity Provider REST API initialized\n"));
2048   return api;
2049 }
2050
2051
2052 /**
2053  * Exit point from the plugin.
2054  *
2055  * @param cls the plugin context (as returned by "init")
2056  * @return always NULL
2057  */
2058 void *
2059 libgnunet_plugin_rest_openid_connect_done (void *cls)
2060 {
2061   struct GNUNET_REST_Plugin *api = cls;
2062   struct Plugin *plugin = api->cls;
2063   plugin->cfg = NULL;
2064
2065   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2066   void *value = NULL;
2067   hashmap_it =
2068       GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2069   while (GNUNET_YES ==
2070          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2071     GNUNET_free_non_null (value);
2072   GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2073
2074   hashmap_it =
2075       GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2076   while (GNUNET_YES ==
2077          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2078     GNUNET_free_non_null (value);
2079   GNUNET_CONTAINER_multihashmap_destroy (OIDC_identity_grants);
2080
2081   hashmap_it =
2082       GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_used_ticket_map);
2083   while (GNUNET_YES ==
2084          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2085     GNUNET_free_non_null (value);
2086   GNUNET_CONTAINER_multihashmap_destroy (OIDC_used_ticket_map);
2087
2088   hashmap_it =
2089       GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2090   while (GNUNET_YES ==
2091          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2092     GNUNET_free_non_null (value);
2093   GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2094   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2095   GNUNET_free_non_null (allow_methods);
2096   GNUNET_free (api);
2097   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2098               "Identity Provider REST plugin is finished\n");
2099   return NULL;
2100 }
2101
2102 /* end of plugin_rest_identity_provider.c */