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