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