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