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