aboutsummaryrefslogtreecommitdiff
path: root/src/rest-plugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/rest-plugin')
-rw-r--r--src/rest-plugin/Makefile.am5
-rw-r--r--src/rest-plugin/gns/Makefile.am61
-rw-r--r--src/rest-plugin/gns/meson.build13
-rw-r--r--src/rest-plugin/gns/plugin_rest_gns.c485
-rwxr-xr-xsrc/rest-plugin/gns/test_plugin_rest_gns.sh76
-rw-r--r--src/rest-plugin/identity/Makefile.am26
-rw-r--r--src/rest-plugin/identity/meson.build11
-rw-r--r--src/rest-plugin/identity/plugin_rest_identity.c1286
-rw-r--r--src/rest-plugin/meson.build6
-rw-r--r--src/rest-plugin/reclaim/Makefile.am81
-rw-r--r--src/rest-plugin/reclaim/json_reclaim.c398
-rw-r--r--src/rest-plugin/reclaim/json_reclaim.h57
-rw-r--r--src/rest-plugin/reclaim/meson.build29
-rw-r--r--src/rest-plugin/reclaim/oidc_helper.c1026
-rw-r--r--src/rest-plugin/reclaim/oidc_helper.h196
-rw-r--r--src/rest-plugin/reclaim/plugin_rest_openid_connect.c3162
-rw-r--r--src/rest-plugin/reclaim/plugin_rest_pabc.c667
-rw-r--r--src/rest-plugin/reclaim/plugin_rest_reclaim.c1564
-rw-r--r--src/rest-plugin/rest/Makefile.am37
-rw-r--r--src/rest-plugin/rest/meson.build18
-rw-r--r--src/rest-plugin/rest/plugin_rest_config.c455
-rw-r--r--src/rest-plugin/rest/plugin_rest_copying.c238
22 files changed, 0 insertions, 9897 deletions
diff --git a/src/rest-plugin/Makefile.am b/src/rest-plugin/Makefile.am
deleted file mode 100644
index d76972b78..000000000
--- a/src/rest-plugin/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
1SUBDIRS = \
2 rest \
3 identity \
4 gns \
5 reclaim
diff --git a/src/rest-plugin/gns/Makefile.am b/src/rest-plugin/gns/Makefile.am
deleted file mode 100644
index 48b278df6..000000000
--- a/src/rest-plugin/gns/Makefile.am
+++ /dev/null
@@ -1,61 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if HAVE_LIBIDN
5 LIBIDN= -lidn
6else
7 LIBIDN=
8endif
9
10if HAVE_LIBIDN2
11 LIBIDN2= -lidn2
12else
13 LIBIDN2=
14endif
15
16USE_VPN = $(top_builddir)/src/service/vpn/libgnunetvpn.la
17
18if USE_COVERAGE
19 AM_CFLAGS = --coverage -O0
20endif
21
22pkgcfgdir = $(pkgdatadir)/config.d/
23
24libexecdir= $(pkglibdir)/libexec/
25
26plugindir = $(libdir)/gnunet
27
28REST_PLUGIN = libgnunet_plugin_rest_gns.la
29
30plugin_LTLIBRARIES = \
31 $(REST_PLUGIN)
32
33
34libgnunet_plugin_rest_gns_la_SOURCES = \
35 plugin_rest_gns.c
36libgnunet_plugin_rest_gns_la_LIBADD = \
37 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
38 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecordjson.la \
39 $(top_builddir)/src/service/gns/libgnunetgns.la \
40 $(top_builddir)/src/service/rest/libgnunetrest.la \
41 $(top_builddir)/src/service/identity/libgnunetidentity.la \
42 $(top_builddir)/src/lib/json/libgnunetjson.la \
43 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
44 $(LTLIBINTL) -ljansson $(MHD_LIBS)
45libgnunet_plugin_rest_gns_la_LDFLAGS = \
46 $(GN_PLUGIN_LDFLAGS)
47libgnunet_plugin_rest_gns_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
48
49
50check_SCRIPTS = \
51 test_plugin_rest_gns.sh
52
53EXTRA_DIST = \
54 test_plugin_rest_gns.sh
55
56if ENABLE_TEST_RUN
57if HAVE_SQLITE
58 AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
59 TESTS = $(check_SCRIPTS)
60endif
61endif
diff --git a/src/rest-plugin/gns/meson.build b/src/rest-plugin/gns/meson.build
deleted file mode 100644
index 7a4deffed..000000000
--- a/src/rest-plugin/gns/meson.build
+++ /dev/null
@@ -1,13 +0,0 @@
1shared_module('gnunet_plugin_rest_gns',
2 ['plugin_rest_gns.c'],
3 dependencies: [libgnunetrest_dep,
4 libgnunetgnsrecord_dep,
5 libgnunetgnsrecordjson_dep,
6 libgnunetgns_dep,
7 libgnunetutil_dep,
8 json_dep,
9 mhd_dep],
10 include_directories: [incdir, configuration_inc],
11 install: true,
12 install_dir: get_option('libdir') / 'gnunet')
13
diff --git a/src/rest-plugin/gns/plugin_rest_gns.c b/src/rest-plugin/gns/plugin_rest_gns.c
deleted file mode 100644
index 659b77493..000000000
--- a/src/rest-plugin/gns/plugin_rest_gns.c
+++ /dev/null
@@ -1,485 +0,0 @@
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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Philippe Buschmann
22 * @file gns/plugin_rest_gns.c
23 * @brief GNUnet Gns REST plugin
24 */
25
26#include "platform.h"
27#include "gnunet_rest_plugin.h"
28#include "gnunet_rest_lib.h"
29#include "gnunet_json_lib.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "gnunet_gnsrecord_json_lib.h"
32#include "gnunet_gns_service.h"
33#include "microhttpd.h"
34#include <jansson.h>
35
36/**
37 * Rest API GNS Namespace
38 */
39#define GNUNET_REST_API_NS_GNS "/gns"
40
41/**
42 * Rest API GNS Parameter record_type
43 */
44#define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
45
46/**
47 * Rest API GNS ERROR Unknown Error
48 */
49#define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
50
51/**
52 * Rest API GNS ERROR Record not found
53 */
54#define GNUNET_REST_GNS_NOT_FOUND "Record not found"
55
56/**
57 * The configuration handle
58 */
59const struct GNUNET_CONFIGURATION_Handle *cfg;
60
61/**
62 * HTTP methods allows for this plugin
63 */
64static char *allow_methods;
65
66/**
67 * Connection to GNS
68 */
69static struct GNUNET_GNS_Handle *gns;
70
71/**
72 * @brief struct returned by the initialization function of the plugin
73 */
74struct Plugin
75{
76 const struct GNUNET_CONFIGURATION_Handle *cfg;
77};
78
79/**
80 * The request handle
81 */
82struct RequestHandle
83{
84 /**
85 * DLL
86 */
87 struct RequestHandle *next;
88
89 /**
90 * DLL
91 */
92 struct RequestHandle *prev;
93
94 /**
95 * Active GNS lookup
96 */
97 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
98
99 /**
100 * Name to look up
101 */
102 char *name;
103
104 /**
105 * Record type to look up
106 */
107 int record_type;
108
109 /**
110 * Rest connection
111 */
112 struct GNUNET_REST_RequestHandle *rest_handle;
113
114 /**
115 * Desired timeout for the lookup (default is no timeout).
116 */
117 struct GNUNET_TIME_Relative timeout;
118
119 /**
120 * ID of a task associated with the resolution process.
121 */
122 struct GNUNET_SCHEDULER_Task *timeout_task;
123
124 /**
125 * The plugin result processor
126 */
127 GNUNET_REST_ResultProcessor proc;
128
129 /**
130 * The closure of the result processor
131 */
132 void *proc_cls;
133
134 /**
135 * The url
136 */
137 char *url;
138
139 /**
140 * Error response message
141 */
142 char *emsg;
143
144 /**
145 * Response code
146 */
147 int response_code;
148};
149
150/**
151 * DLL
152 */
153static struct RequestHandle *requests_head;
154
155/**
156 * DLL
157 */
158static struct RequestHandle *requests_tail;
159
160/**
161 * Cleanup lookup handle
162 * @param cls `struct RequestHandle` to clean up
163 */
164static void
165cleanup_handle (void *cls)
166{
167 struct RequestHandle *handle = cls;
168
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
170
171 if (NULL != handle->gns_lookup)
172 {
173 GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
174 handle->gns_lookup = NULL;
175 }
176 if (NULL != handle->timeout_task)
177 {
178 GNUNET_SCHEDULER_cancel (handle->timeout_task);
179 handle->timeout_task = NULL;
180 }
181 if (NULL != handle->url)
182 GNUNET_free (handle->url);
183 if (NULL != handle->name)
184 GNUNET_free (handle->name);
185 if (NULL != handle->emsg)
186 GNUNET_free (handle->emsg);
187
188 GNUNET_CONTAINER_DLL_remove (requests_head,
189 requests_tail,
190 handle);
191 GNUNET_free (handle);
192}
193
194
195/**
196 * Task run on errors. Reports an error and cleans up everything.
197 *
198 * @param cls the `struct RequestHandle`
199 */
200static void
201do_error (void *cls)
202{
203 struct RequestHandle *handle = cls;
204 struct MHD_Response *resp;
205 json_t *json_error = json_object ();
206 char *response;
207
208 if (NULL != handle->timeout_task)
209 GNUNET_SCHEDULER_cancel (handle->timeout_task);
210 handle->timeout_task = NULL;
211 if (NULL == handle->emsg)
212 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_ERROR_UNKNOWN);
213
214 json_object_set_new (json_error, "error", json_string (handle->emsg));
215
216 if (0 == handle->response_code)
217 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
218 response = json_dumps (json_error, 0);
219 resp = GNUNET_REST_create_response (response);
220 MHD_add_response_header (resp, "Content-Type", "application/json");
221 handle->proc (handle->proc_cls, resp, handle->response_code);
222 json_decref (json_error);
223 GNUNET_free (response);
224 cleanup_handle(handle);
225}
226
227
228static void
229do_timeout (void *cls)
230{
231 struct RequestHandle *handle = cls;
232
233 handle->timeout_task = NULL;
234 handle->response_code = MHD_HTTP_REQUEST_TIMEOUT;
235 do_error (handle);
236}
237
238
239/**
240 * Iterator called on obtained result for a GNS lookup.
241 *
242 * @param cls closure with the object
243 * @param was_gns #GNUNET_NO if name was not a GNS name
244 * @param rd_count number of records in @a rd
245 * @param rd the records in reply
246 */
247static void
248handle_gns_response (void *cls,
249 int was_gns,
250 uint32_t rd_count,
251 const struct GNUNET_GNSRECORD_Data *rd)
252{
253 struct RequestHandle *handle = cls;
254 struct MHD_Response *resp;
255 json_t *result_obj;
256 char *result;
257
258 handle->gns_lookup = NULL;
259
260 if (GNUNET_NO == was_gns)
261 {
262 handle->response_code = MHD_HTTP_NOT_FOUND;
263 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
264 GNUNET_SCHEDULER_add_now (&do_error, handle);
265 return;
266 }
267
268 result_obj = GNUNET_GNSRECORD_JSON_from_gnsrecord (handle->name, rd, rd_count);
269
270 result = json_dumps (result_obj, 0);
271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
272 resp = GNUNET_REST_create_response (result);
273 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
274 "Content-Type",
275 "application/json"));
276 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
277 GNUNET_free (result);
278 json_decref (result_obj);
279 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
280}
281
282
283/**
284 * Handle gns GET request
285 *
286 * @param con_handle the connection handle
287 * @param url the url
288 * @param cls the RequestHandle
289 */
290void
291get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
292 const char *url,
293 void *cls)
294{
295 struct RequestHandle *handle = cls;
296 struct GNUNET_HashCode key;
297 char *record_type;
298 char *name;
299
300 name = NULL;
301 handle->name = NULL;
302 if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url))
303 {
304 name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1];
305 }
306
307 if (NULL == name)
308 {
309 handle->response_code = MHD_HTTP_NOT_FOUND;
310 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
311 GNUNET_SCHEDULER_add_now (&do_error, handle);
312 return;
313 }
314 if (0 >= strlen (name))
315 {
316 handle->response_code = MHD_HTTP_NOT_FOUND;
317 handle->emsg = GNUNET_strdup (GNUNET_REST_GNS_NOT_FOUND);
318 GNUNET_SCHEDULER_add_now (&do_error, handle);
319 return;
320 }
321 handle->name = GNUNET_strdup (name);
322
323 handle->record_type = UINT32_MAX;
324 GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
325 strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
326 &key);
327 if (GNUNET_YES ==
328 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
329 {
330 record_type =
331 GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
332 handle->record_type = GNUNET_GNSRECORD_typename_to_number (record_type);
333 }
334
335 if (UINT32_MAX == handle->record_type)
336 {
337 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
338 }
339
340 handle->gns_lookup = GNUNET_GNS_lookup_with_tld (gns,
341 handle->name,
342 handle->record_type,
343 GNUNET_GNS_LO_DEFAULT,
344 &handle_gns_response,
345 handle);
346}
347
348
349/**
350 * Respond to OPTIONS request
351 *
352 * @param con_handle the connection handle
353 * @param url the url
354 * @param cls the RequestHandle
355 */
356static void
357options_cont (struct GNUNET_REST_RequestHandle *con_handle,
358 const char *url,
359 void *cls)
360{
361 struct MHD_Response *resp;
362 struct RequestHandle *handle = cls;
363
364 // independent of path return all options
365 resp = GNUNET_REST_create_response (NULL);
366 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
367 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
368 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
369 return;
370}
371
372
373/**
374 * Function processing the REST call
375 *
376 * @param method HTTP method
377 * @param url URL of the HTTP request
378 * @param data body of the HTTP request (optional)
379 * @param data_size length of the body
380 * @param proc callback function for the result
381 * @param proc_cls closure for callback function
382 * @return GNUNET_OK if request accepted
383 */
384static enum GNUNET_GenericReturnValue
385rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
386 GNUNET_REST_ResultProcessor proc,
387 void *proc_cls)
388{
389 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
390 struct GNUNET_REST_RequestHandlerError err;
391 static const struct GNUNET_REST_RequestHandler handlers[] =
392 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont },
393 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont },
394 GNUNET_REST_HANDLER_END };
395
396 handle->response_code = 0;
397 handle->timeout =
398 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
399 handle->proc_cls = proc_cls;
400 handle->proc = proc;
401 handle->rest_handle = rest_handle;
402 handle->url = GNUNET_strdup (rest_handle->url);
403 handle->timeout_task =
404 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
405 GNUNET_CONTAINER_DLL_insert (requests_head,
406 requests_tail,
407 handle);
408 if (handle->url[strlen (handle->url) - 1] == '/')
409 handle->url[strlen (handle->url) - 1] = '\0';
410 if (GNUNET_NO ==
411 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
412 {
413 cleanup_handle (handle);
414 return GNUNET_NO;
415 }
416
417
418 return GNUNET_YES;
419}
420
421
422/**
423 * Entry point for the plugin.
424 *
425 * @param cls Config info
426 * @return NULL on error, otherwise the plugin context
427 */
428void *
429libgnunet_plugin_rest_gns_init (void *cls)
430{
431 static struct Plugin plugin;
432 struct GNUNET_REST_Plugin *api;
433
434 cfg = cls;
435 memset (&plugin, 0, sizeof(struct Plugin));
436 plugin.cfg = cfg;
437 api = GNUNET_new (struct GNUNET_REST_Plugin);
438 api->cls = &plugin;
439 api->name = GNUNET_REST_API_NS_GNS;
440 api->process_request = &rest_process_request;
441 GNUNET_asprintf (&allow_methods,
442 "%s, %s, %s, %s, %s",
443 MHD_HTTP_METHOD_GET,
444 MHD_HTTP_METHOD_POST,
445 MHD_HTTP_METHOD_PUT,
446 MHD_HTTP_METHOD_DELETE,
447 MHD_HTTP_METHOD_OPTIONS);
448 gns = GNUNET_GNS_connect (cfg);
449
450 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Gns REST API initialized\n"));
451 return api;
452}
453
454
455/**
456 * Exit point from the plugin.
457 *
458 * @param cls the plugin context (as returned by "init")
459 * @return always NULL
460 */
461void *
462libgnunet_plugin_rest_gns_done (void *cls)
463{
464 struct GNUNET_REST_Plugin *api = cls;
465 struct RequestHandle *request;
466 struct Plugin *plugin;
467
468 while (NULL != (request = requests_head))
469 do_error (request);
470
471 if (NULL != gns)
472 GNUNET_GNS_disconnect (gns);
473
474 plugin = api->cls;
475
476 plugin->cfg = NULL;
477
478 GNUNET_free (allow_methods);
479 GNUNET_free (api);
480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Gns REST plugin is finished\n");
481 return NULL;
482}
483
484
485/* end of plugin_rest_gns.c */
diff --git a/src/rest-plugin/gns/test_plugin_rest_gns.sh b/src/rest-plugin/gns/test_plugin_rest_gns.sh
deleted file mode 100755
index 3bcfb5a08..000000000
--- a/src/rest-plugin/gns/test_plugin_rest_gns.sh
+++ /dev/null
@@ -1,76 +0,0 @@
1#!/bin/sh
2# This file is in the public domain.
3trap "gnunet-arm -e -c test_gns_lookup.conf" INT
4LOCATION=$(which gnunet-config)
5if [ -z $LOCATION ]
6then
7 LOCATION="gnunet-config"
8fi
9$LOCATION --version 1> /dev/null
10if test $? != 0
11then
12 echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX"
13 exit 77
14fi
15
16rm -rf `gnunet-config -c test_gns_lookup.conf -f -s paths -o GNUNET_TEST_HOME`
17
18gns_link="http://localhost:7776/gns"
19wrong_link="http://localhost:7776/gnsandmore"
20
21curl_get () {
22 #$1 is link
23 #$2 is grep
24 XURL=`which curl`
25 if [ "" = "$XURL" ]
26 then
27 echo "HTTP client (curl) not found, exiting"
28 exit 77
29 fi
30 sleep 0.5
31 cache="$(${XURL} -v "$1" 2>&1 | grep "$2")"
32 #echo "$cache"
33 if [ "" = "$cache" ]
34 then
35 gnunet-identity -D "$TEST_TLD" -c test_gns_lookup.conf > /dev/null 2>&1
36 gnunet-arm -e -c test_gns_lookup.conf
37 echo "Download of $1 using $XURL failed, expected $2"
38 exit 1
39 fi
40}
41TEST_TLD="testtld"
42
43gnunet-arm -s -c test_gns_lookup.conf
44
45curl_get "$gns_link/www.$TEST_TLD" "error"
46
47gnunet-identity -C "$TEST_TLD" -c test_gns_lookup.conf
48sleep 0.5
49curl_get "$gns_link/www.$TEST_TLD" "\[\]"
50
51gnunet-namestore -z "$TEST_TLD" -p -a -n www -e 1d -V 1.1.1.1 -t A -c test_gns_lookup.conf
52
53curl_get "$gns_link/www.$TEST_TLD" "1.1.1.1"
54
55gnunet-namestore -z "$TEST_TLD" -p -a -n www -e 1d -V 1::1 -t AAAA -c test_gns_lookup.conf
56
57curl_get "$gns_link/www.$TEST_TLD" "1::1.*1.1.1.1"
58
59gnunet-namestore -z "$TEST_TLD" -p -a -n www -e 1d -V 1.1.1.2 -t A -c test_gns_lookup.conf
60
61curl_get "$gns_link/www.$TEST_TLD" "1.1.1.2.*1::1.*1.1.1.1"
62curl_get "$gns_link/www.$TEST_TLD?record_type=A" "1.1.1.2.*1.1.1.1"
63curl_get "$gns_link/www.$TEST_TLD?record_type=AAAA" "1::1"
64curl_get "$gns_link/www.$TEST_TLD?record_type=WRONG_TYPE" "1.1.1.2.*1::1.*1.1.1.1"
65
66gnunet-namestore -z "$TEST_TLD" -p -a -n www1 -e 1d -V 1.1.1.1 -t A -c test_gns_lookup.conf
67curl_get "$gns_link/www1.$TEST_TLD" "1.1.1.1"
68
69gnunet-namestore -z "$TEST_TLD" -d -n www1 -c test_gns_lookup.conf
70gnunet-namestore -z "$TEST_TLD" -d -n www -c test_gns_lookup.conf
71
72gnunet-identity -D "$TEST_TLD" -c test_gns_lookup.conf > /dev/null 2>&1
73
74curl_get "$gns_link/www1.$TEST_TLD" "error"
75gnunet-arm -e -c test_gns_lookup.conf
76exit 0
diff --git a/src/rest-plugin/identity/Makefile.am b/src/rest-plugin/identity/Makefile.am
deleted file mode 100644
index bcad0c2dd..000000000
--- a/src/rest-plugin/identity/Makefile.am
+++ /dev/null
@@ -1,26 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6if USE_COVERAGE
7 AM_CFLAGS = --coverage -O0
8 XLIB = -lgcov
9endif
10
11plugin_LTLIBRARIES = libgnunet_plugin_rest_identity.la
12
13pkgcfgdir= $(pkgdatadir)/config.d/
14
15libexecdir= $(pkglibdir)/libexec/
16
17libgnunet_plugin_rest_identity_la_SOURCES = \
18 plugin_rest_identity.c
19libgnunet_plugin_rest_identity_la_LIBADD = \
20 $(top_builddir)/src/service/identity/libgnunetidentity.la \
21 $(top_builddir)/src/service/rest/libgnunetrest.la \
22 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
23 $(LTLIBINTL) -ljansson $(MHD_LIBS)
24libgnunet_plugin_rest_identity_la_LDFLAGS = \
25 $(GN_PLUGIN_LDFLAGS)
26libgnunet_plugin_rest_identity_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
diff --git a/src/rest-plugin/identity/meson.build b/src/rest-plugin/identity/meson.build
deleted file mode 100644
index f459918db..000000000
--- a/src/rest-plugin/identity/meson.build
+++ /dev/null
@@ -1,11 +0,0 @@
1shared_module('gnunet_plugin_rest_identity',
2 ['plugin_rest_identity.c'],
3 dependencies: [libgnunetrest_dep,
4 libgnunetidentity_dep,
5 libgnunetutil_dep,
6 json_dep,
7 mhd_dep],
8 include_directories: [incdir, configuration_inc],
9 install: true,
10 install_dir: get_option('libdir') / 'gnunet')
11
diff --git a/src/rest-plugin/identity/plugin_rest_identity.c b/src/rest-plugin/identity/plugin_rest_identity.c
deleted file mode 100644
index e7b7f8a9c..000000000
--- a/src/rest-plugin/identity/plugin_rest_identity.c
+++ /dev/null
@@ -1,1286 +0,0 @@
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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file identity/plugin_rest_identity.c
24 * @brief GNUnet Identity REST plugin
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include "gnunet_identity_service.h"
30#include "gnunet_rest_lib.h"
31#include "../../service/identity/identity.h"
32#include "gnunet_util_lib.h"
33#include "microhttpd.h"
34#include <jansson.h>
35
36/**
37 * Identity Namespace
38 */
39#define GNUNET_REST_API_NS_IDENTITY "/identity"
40
41/**
42 * Identity Namespace with public key specifier
43 */
44#define GNUNET_REST_API_NS_IDENTITY_PUBKEY "/identity/pubkey"
45
46/**
47 * Identity Namespace with public key specifier
48 */
49#define GNUNET_REST_API_NS_IDENTITY_NAME "/identity/name"
50
51/**
52 * Identity Namespace with sign specifier
53 */
54#define GNUNET_REST_API_NS_SIGN "/sign"
55
56/**
57 * Parameter public key
58 */
59#define GNUNET_REST_IDENTITY_PARAM_PUBKEY "pubkey"
60
61/**
62 * Parameter private key
63 */
64#define GNUNET_REST_IDENTITY_PARAM_PRIVKEY "privkey"
65
66/**
67 * Parameter name
68 */
69#define GNUNET_REST_IDENTITY_PARAM_NAME "name"
70
71/**
72 * Parameter new name
73 */
74#define GNUNET_REST_IDENTITY_PARAM_NEWNAME "newname"
75
76/**
77 * Error message Missing identity name
78 */
79#define GNUNET_REST_IDENTITY_MISSING_NAME "Missing identity name"
80
81/**
82 * Error message Missing identity name
83 */
84#define GNUNET_REST_IDENTITY_MISSING_PUBKEY "Missing identity public key"
85
86/**
87 * Error message No data
88 */
89#define GNUNET_REST_ERROR_NO_DATA "No data"
90
91/**
92 * Error message Data invalid
93 */
94#define GNUNET_REST_ERROR_DATA_INVALID "Data invalid"
95
96/**
97 * State while collecting all egos
98 */
99#define ID_REST_STATE_INIT 0
100
101/**
102 * Done collecting egos
103 */
104#define ID_REST_STATE_POST_INIT 1
105
106/**
107 * The configuration handle
108 */
109const struct GNUNET_CONFIGURATION_Handle *cfg;
110
111/**
112 * HTTP methods allows for this plugin
113 */
114static char *allow_methods;
115
116/**
117 * Ego list
118 */
119static struct EgoEntry *ego_head;
120
121/**
122 * Ego list
123 */
124static struct EgoEntry *ego_tail;
125
126/**
127 * The processing state
128 */
129static int state;
130
131/**
132 * Handle to Identity service.
133 */
134static struct GNUNET_IDENTITY_Handle *identity_handle;
135
136/**
137 * @brief struct returned by the initialization function of the plugin
138 */
139struct Plugin
140{
141 const struct GNUNET_CONFIGURATION_Handle *cfg;
142};
143
144/**
145 * The ego list
146 */
147struct EgoEntry
148{
149 /**
150 * DLL
151 */
152 struct EgoEntry *next;
153
154 /**
155 * DLL
156 */
157 struct EgoEntry *prev;
158
159 /**
160 * Ego Identifier
161 */
162 char *identifier;
163
164 /**
165 * Public key string
166 */
167 char *keystring;
168
169 /**
170 * The Ego
171 */
172 struct GNUNET_IDENTITY_Ego *ego;
173};
174
175/**
176 * The request handle
177 */
178struct RequestHandle
179{
180 /**
181 * DLL
182 */
183 struct RequestHandle *next;
184
185 /**
186 * DLL
187 */
188 struct RequestHandle *prev;
189
190 /**
191 * The data from the REST request
192 */
193 const char *data;
194
195 /**
196 * The name to look up
197 */
198 char *name;
199
200 /**
201 * the length of the REST data
202 */
203 size_t data_size;
204
205 /**
206 * IDENTITY Operation
207 */
208 struct GNUNET_IDENTITY_Operation *op;
209
210 /**
211 * Rest connection
212 */
213 struct GNUNET_REST_RequestHandle *rest_handle;
214
215 /**
216 * Desired timeout for the lookup (default is no timeout).
217 */
218 struct GNUNET_TIME_Relative timeout;
219
220 /**
221 * ID of a task associated with the resolution process.
222 */
223 struct GNUNET_SCHEDULER_Task *timeout_task;
224
225 /**
226 * The plugin result processor
227 */
228 GNUNET_REST_ResultProcessor proc;
229
230 /**
231 * The closure of the result processor
232 */
233 void *proc_cls;
234
235 /**
236 * The url
237 */
238 char *url;
239
240 /**
241 * Error code
242 */
243 enum GNUNET_ErrorCode ec;
244};
245
246/**
247 * DLL
248 */
249static struct RequestHandle *requests_head;
250
251/**
252 * DLL
253 */
254static struct RequestHandle *requests_tail;
255
256/**
257 * Cleanup lookup handle
258 * @param cls Handle to clean up
259 */
260static void
261cleanup_handle (void *cls)
262{
263 struct RequestHandle *handle = cls;
264
265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
266 if (NULL != handle->timeout_task)
267 {
268 GNUNET_SCHEDULER_cancel (handle->timeout_task);
269 handle->timeout_task = NULL;
270 }
271
272 if (NULL != handle->url)
273 GNUNET_free (handle->url);
274 if (NULL != handle->name)
275 GNUNET_free (handle->name);
276 GNUNET_CONTAINER_DLL_remove (requests_head,
277 requests_tail,
278 handle);
279 GNUNET_free (handle);
280}
281
282
283/**
284 * Task run on errors. Reports an error and cleans up everything.
285 *
286 * @param cls the `struct RequestHandle`
287 */
288static void
289do_error (void *cls)
290{
291 struct RequestHandle *handle = cls;
292 struct MHD_Response *resp;
293 json_t *json_error = json_object ();
294 char *response;
295 int response_code;
296
297 json_object_set_new (json_error, "error",
298 json_string (GNUNET_ErrorCode_get_hint (handle->ec)));
299 json_object_set_new (json_error, "error_code", json_integer (handle->ec));
300 response_code = GNUNET_ErrorCode_get_http_status (handle->ec);
301 if (0 == response_code)
302 response_code = MHD_HTTP_OK;
303 response = json_dumps (json_error, 0);
304 resp = GNUNET_REST_create_response (response);
305 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
306 "Content-Type",
307 "application/json"));
308 handle->proc (handle->proc_cls, resp, response_code);
309 json_decref (json_error);
310 GNUNET_free (response);
311 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
312}
313
314
315/**
316 * Get EgoEntry from list with either a public key or a name
317 * If public key and name are not NULL, it returns the public key result first
318 *
319 * @param handle the RequestHandle
320 * @param pubkey the public key of an identity (only one can be NULL)
321 * @param name the name of an identity (only one can be NULL)
322 * @return EgoEntry or NULL if not found
323 */
324struct EgoEntry *
325get_egoentry (struct RequestHandle *handle, char *pubkey, char *name)
326{
327 struct EgoEntry *ego_entry;
328
329 if (NULL != pubkey)
330 {
331 for (ego_entry = ego_head; NULL != ego_entry;
332 ego_entry = ego_entry->next)
333 {
334 if (0 != strcasecmp (pubkey, ego_entry->keystring))
335 continue;
336 return ego_entry;
337 }
338 }
339 if (NULL != name)
340 {
341 for (ego_entry = ego_head; NULL != ego_entry;
342 ego_entry = ego_entry->next)
343 {
344 if (0 != strcasecmp (name, ego_entry->identifier))
345 continue;
346 return ego_entry;
347 }
348 }
349 return NULL;
350}
351
352
353/**
354 * Handle identity GET request - responds with all identities
355 *
356 * @param con_handle the connection handle
357 * @param url the url
358 * @param cls the RequestHandle
359 */
360void
361ego_get_all (struct GNUNET_REST_RequestHandle *con_handle,
362 const char *url,
363 void *cls)
364{
365 struct RequestHandle *handle = cls;
366 struct EgoEntry *ego_entry;
367 struct MHD_Response *resp;
368 struct GNUNET_HashCode key;
369 json_t *json_root;
370 json_t *json_ego;
371 char *result_str;
372 char *privkey_str;
373
374 json_root = json_array ();
375 // Return ego/egos
376 for (ego_entry = ego_head; NULL != ego_entry;
377 ego_entry = ego_entry->next)
378 {
379 json_ego = json_object ();
380 json_object_set_new (json_ego,
381 GNUNET_REST_IDENTITY_PARAM_PUBKEY,
382 json_string (ego_entry->keystring));
383 GNUNET_CRYPTO_hash ("private", strlen ("private"), &key);
384 if (GNUNET_YES ==
385 GNUNET_CONTAINER_multihashmap_contains (
386 handle->rest_handle->url_param_map, &key))
387 {
388 privkey_str = GNUNET_CRYPTO_private_key_to_string (
389 GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego));
390 json_object_set_new (json_ego,
391 GNUNET_REST_IDENTITY_PARAM_PRIVKEY,
392 json_string (privkey_str));
393 GNUNET_free (privkey_str);
394 }
395
396 json_object_set_new (json_ego,
397 GNUNET_REST_IDENTITY_PARAM_NAME,
398 json_string (ego_entry->identifier));
399 json_array_append (json_root, json_ego);
400 json_decref (json_ego);
401 }
402
403 result_str = json_dumps (json_root, 0);
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
405 resp = GNUNET_REST_create_response (result_str);
406 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
407 "Content-Type",
408 "application/json"));
409 json_decref (json_root);
410 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
411 GNUNET_free (result_str);
412 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
413}
414
415
416/**
417 * Responds with the ego_entry identity
418 *
419 * @param handle the struct RequestHandle
420 * @param ego_entry the struct EgoEntry for the response
421 */
422void
423ego_get_response (struct RequestHandle *handle, struct EgoEntry *ego_entry)
424{
425 struct MHD_Response *resp;
426 struct GNUNET_HashCode key;
427 json_t *json_ego;
428 char *result_str;
429 char *privkey_str;
430
431 json_ego = json_object ();
432 json_object_set_new (json_ego,
433 GNUNET_REST_IDENTITY_PARAM_PUBKEY,
434 json_string (ego_entry->keystring));
435 json_object_set_new (json_ego,
436 GNUNET_REST_IDENTITY_PARAM_NAME,
437 json_string (ego_entry->identifier));
438 GNUNET_CRYPTO_hash ("private", strlen ("private"), &key);
439 if (GNUNET_YES ==
440 GNUNET_CONTAINER_multihashmap_contains (
441 handle->rest_handle->url_param_map, &key))
442 {
443 privkey_str = GNUNET_CRYPTO_private_key_to_string (
444 GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego));
445 json_object_set_new (json_ego,
446 GNUNET_REST_IDENTITY_PARAM_PRIVKEY,
447 json_string (privkey_str));
448 GNUNET_free (privkey_str);
449 }
450
451 result_str = json_dumps (json_ego, 0);
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
453 resp = GNUNET_REST_create_response (result_str);
454 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
455 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
456 "Content-Type",
457 "application/json"));
458 json_decref (json_ego);
459 GNUNET_free (result_str);
460 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
461}
462
463
464/**
465 * Handle identity GET request with a public key
466 *
467 * @param con_handle the connection handle
468 * @param url the url
469 * @param cls the RequestHandle
470 */
471void
472ego_get_pubkey (struct GNUNET_REST_RequestHandle *con_handle,
473 const char *url,
474 void *cls)
475{
476 struct RequestHandle *handle = cls;
477 struct EgoEntry *ego_entry;
478 char *keystring;
479
480 keystring = NULL;
481
482 if (strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) >= strlen (handle->url))
483 {
484 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
485 GNUNET_SCHEDULER_add_now (&do_error, handle);
486 return;
487 }
488 keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) + 1];
489 ego_entry = get_egoentry (handle, keystring, NULL);
490
491 if (NULL == ego_entry)
492 {
493 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
494 GNUNET_SCHEDULER_add_now (&do_error, handle);
495 return;
496 }
497
498 ego_get_response (handle, ego_entry);
499}
500
501
502/**
503 * Handle identity GET request with a name
504 *
505 * @param con_handle the connection handle
506 * @param url the url
507 * @param cls the RequestHandle
508 */
509void
510ego_get_name (struct GNUNET_REST_RequestHandle *con_handle,
511 const char *url,
512 void *cls)
513{
514 struct RequestHandle *handle = cls;
515 struct EgoEntry *ego_entry;
516 char *egoname;
517
518 egoname = NULL;
519
520 if (strlen (GNUNET_REST_API_NS_IDENTITY_NAME) >= strlen (handle->url))
521 {
522 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
523 GNUNET_SCHEDULER_add_now (&do_error, handle);
524 return;
525 }
526 egoname = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_NAME) + 1];
527 ego_entry = get_egoentry (handle, NULL, egoname);
528
529 if (NULL == ego_entry)
530 {
531 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
532 GNUNET_SCHEDULER_add_now (&do_error, handle);
533 return;
534 }
535
536 ego_get_response (handle, ego_entry);
537}
538
539
540/**
541 * Processing finished
542 *
543 * @param cls request handle
544 * @param ec error code
545 */
546static void
547do_finished (void *cls, enum GNUNET_ErrorCode ec)
548{
549 struct RequestHandle *handle = cls;
550 struct MHD_Response *resp;
551 int response_code;
552
553 handle->op = NULL;
554 handle->ec = ec;
555 if (GNUNET_EC_NONE != ec)
556 {
557 GNUNET_SCHEDULER_add_now (&do_error, handle);
558 return;
559 }
560 if (GNUNET_EC_NONE == handle->ec)
561 response_code = MHD_HTTP_NO_CONTENT;
562 else
563 response_code = GNUNET_ErrorCode_get_http_status (ec);
564 resp = GNUNET_REST_create_response (NULL);
565 handle->proc (handle->proc_cls, resp, response_code);
566 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
567}
568
569
570/**
571 * Processing finished, when creating an ego.
572 *
573 * @param cls request handle
574 * @param pk private key of the ego, or NULL on error
575 * @param ec error code
576 */
577static void
578do_finished_create (void *cls,
579 const struct GNUNET_CRYPTO_PrivateKey *pk,
580 enum GNUNET_ErrorCode ec)
581{
582 struct RequestHandle *handle = cls;
583
584 (void) pk;
585 do_finished (handle, ec);
586}
587
588
589/**
590 * Processing edit ego with EgoEntry ego_entry
591 *
592 * @param handle the struct RequestHandle
593 * @param ego_entry the struct EgoEntry we want to edit
594 */
595void
596ego_edit (struct RequestHandle *handle, struct EgoEntry *ego_entry)
597{
598 json_t *data_js;
599 json_error_t err;
600 char *newname;
601 char term_data[handle->data_size + 1];
602 int json_state;
603
604 // if no data
605 if (0 >= handle->data_size)
606 {
607 handle->ec = GNUNET_EC_IDENTITY_INVALID;
608 GNUNET_SCHEDULER_add_now (&do_error, handle);
609 return;
610 }
611 // if not json
612 term_data[handle->data_size] = '\0';
613 GNUNET_memcpy (term_data, handle->data, handle->data_size);
614 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
615
616 if (NULL == data_js)
617 {
618 handle->ec = GNUNET_EC_IDENTITY_INVALID;
619 GNUNET_SCHEDULER_add_now (&do_error, handle);
620 return;
621 }
622
623 newname = NULL;
624 // NEW NAME
625 json_state = 0;
626 json_state = json_unpack (data_js,
627 "{s:s!}",
628 GNUNET_REST_IDENTITY_PARAM_NEWNAME,
629 &newname);
630 // Change name with pubkey or name identifier
631 if (0 != json_state)
632 {
633 handle->ec = GNUNET_EC_IDENTITY_INVALID;
634 GNUNET_SCHEDULER_add_now (&do_error, handle);
635 json_decref (data_js);
636 return;
637 }
638
639 if (NULL == newname)
640 {
641 handle->ec = GNUNET_EC_IDENTITY_INVALID;
642 GNUNET_SCHEDULER_add_now (&do_error, handle);
643 json_decref (data_js);
644 return;
645 }
646
647 if (0 >= strlen (newname))
648 {
649 handle->ec = GNUNET_EC_IDENTITY_INVALID;
650 GNUNET_SCHEDULER_add_now (&do_error, handle);
651 json_decref (data_js);
652 return;
653 }
654
655 handle->op = GNUNET_IDENTITY_rename (identity_handle,
656 ego_entry->identifier,
657 newname,
658 &do_finished,
659 handle);
660 if (NULL == handle->op)
661 {
662 handle->ec = GNUNET_EC_UNKNOWN;
663 GNUNET_SCHEDULER_add_now (&do_error, handle);
664 json_decref (data_js);
665 return;
666 }
667 json_decref (data_js);
668 return;
669}
670
671
672/**
673 * Handle identity PUT request with public key
674 *
675 * @param con_handle the connection handle
676 * @param url the url
677 * @param cls the RequestHandle
678 */
679void
680ego_edit_pubkey (struct GNUNET_REST_RequestHandle *con_handle,
681 const char *url,
682 void *cls)
683{
684 struct RequestHandle *handle = cls;
685 struct EgoEntry *ego_entry;
686 char *keystring;
687
688 keystring = NULL;
689
690 if (strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) >= strlen (handle->url))
691 {
692 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
693 GNUNET_SCHEDULER_add_now (&do_error, handle);
694 return;
695 }
696 keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) + 1];
697 ego_entry = get_egoentry (handle, keystring, NULL);
698
699 if (NULL == ego_entry)
700 {
701 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
702 GNUNET_SCHEDULER_add_now (&do_error, handle);
703 return;
704 }
705
706 ego_edit (handle, ego_entry);
707}
708
709
710/**
711 * Handle identity PUT request with name
712 *
713 * @param con_handle the connection handle
714 * @param url the url
715 * @param cls the RequestHandle
716 */
717void
718ego_edit_name (struct GNUNET_REST_RequestHandle *con_handle,
719 const char *url,
720 void *cls)
721{
722 struct RequestHandle *handle = cls;
723 struct EgoEntry *ego_entry;
724 char *name;
725
726 name = NULL;
727
728 if (strlen (GNUNET_REST_API_NS_IDENTITY_NAME) >= strlen (handle->url))
729 {
730 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
731 GNUNET_SCHEDULER_add_now (&do_error, handle);
732 return;
733 }
734 name = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_NAME) + 1];
735 ego_entry = get_egoentry (handle, NULL, name);
736
737 if (NULL == ego_entry)
738 {
739 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
740 GNUNET_SCHEDULER_add_now (&do_error, handle);
741 return;
742 }
743
744 ego_edit (handle, ego_entry);
745}
746
747
748/**
749 * Handle identity POST request
750 *
751 * @param con_handle the connection handle
752 * @param url the url
753 * @param cls the RequestHandle
754 */
755void
756ego_create (struct GNUNET_REST_RequestHandle *con_handle,
757 const char *url,
758 void *cls)
759{
760 struct RequestHandle *handle = cls;
761 json_t *data_js;
762 json_error_t err;
763 char *egoname;
764 char *privkey;
765 struct GNUNET_CRYPTO_PrivateKey pk;
766 struct GNUNET_CRYPTO_PrivateKey *pk_ptr;
767 int json_unpack_state;
768 char term_data[handle->data_size + 1];
769
770 if (strlen (GNUNET_REST_API_NS_IDENTITY) != strlen (handle->url))
771 {
772 GNUNET_SCHEDULER_add_now (&do_error, handle);
773 return;
774 }
775
776 if (0 >= handle->data_size)
777 {
778 handle->ec = GNUNET_EC_IDENTITY_INVALID;
779 GNUNET_SCHEDULER_add_now (&do_error, handle);
780 return;
781 }
782 term_data[handle->data_size] = '\0';
783 GNUNET_memcpy (term_data, handle->data, handle->data_size);
784 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
785 if (NULL == data_js)
786 {
787 handle->ec = GNUNET_EC_IDENTITY_INVALID;
788 GNUNET_SCHEDULER_add_now (&do_error, handle);
789 json_decref (data_js);
790 return;
791 }
792 json_unpack_state = 0;
793 privkey = NULL;
794 json_unpack_state =
795 json_unpack (data_js, "{s:s, s?:s!}",
796 GNUNET_REST_IDENTITY_PARAM_NAME, &egoname,
797 GNUNET_REST_IDENTITY_PARAM_PRIVKEY, &privkey);
798 if (0 != json_unpack_state)
799 {
800 handle->ec = GNUNET_EC_IDENTITY_INVALID;
801 GNUNET_SCHEDULER_add_now (&do_error, handle);
802 json_decref (data_js);
803 return;
804 }
805
806 if (NULL == egoname)
807 {
808 handle->ec = GNUNET_EC_IDENTITY_INVALID;
809 GNUNET_SCHEDULER_add_now (&do_error, handle);
810 json_decref (data_js);
811 return;
812 }
813 if (0 >= strlen (egoname))
814 {
815 handle->ec = GNUNET_EC_IDENTITY_INVALID;
816 json_decref (data_js);
817 GNUNET_SCHEDULER_add_now (&do_error, handle);
818 return;
819 }
820 GNUNET_STRINGS_utf8_tolower (egoname, egoname);
821 handle->name = GNUNET_strdup (egoname);
822 if (NULL != privkey)
823 {
824 GNUNET_STRINGS_string_to_data (privkey,
825 strlen (privkey),
826 &pk,
827 sizeof(struct
828 GNUNET_CRYPTO_PrivateKey));
829 pk_ptr = &pk;
830 }
831 else
832 pk_ptr = NULL;
833 json_decref (data_js);
834 handle->op = GNUNET_IDENTITY_create (identity_handle,
835 handle->name,
836 pk_ptr,
837 GNUNET_PUBLIC_KEY_TYPE_ECDSA,
838 &do_finished_create,
839 handle);
840}
841
842
843/**
844 * Handle identity DELETE request with public key
845 *
846 * @param con_handle the connection handle
847 * @param url the url
848 * @param cls the RequestHandle
849 */
850void
851ego_delete_pubkey (struct GNUNET_REST_RequestHandle *con_handle,
852 const char *url,
853 void *cls)
854{
855 struct RequestHandle *handle = cls;
856 struct EgoEntry *ego_entry;
857 char *keystring;
858
859 keystring = NULL;
860
861 if (strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) >= strlen (handle->url))
862 {
863 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
864 GNUNET_SCHEDULER_add_now (&do_error, handle);
865 return;
866 }
867 keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_PUBKEY) + 1];
868 ego_entry = get_egoentry (handle, keystring, NULL);
869
870 if (NULL == ego_entry)
871 {
872 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
873 GNUNET_SCHEDULER_add_now (&do_error, handle);
874 return;
875 }
876
877 handle->op = GNUNET_IDENTITY_delete (identity_handle,
878 ego_entry->identifier,
879 &do_finished,
880 handle);
881}
882
883
884/**
885 * Handle identity DELETE request with name
886 *
887 * @param con_handle the connection handle
888 * @param url the url
889 * @param cls the RequestHandle
890 */
891void
892ego_delete_name (struct GNUNET_REST_RequestHandle *con_handle,
893 const char *url,
894 void *cls)
895{
896 struct RequestHandle *handle = cls;
897 struct EgoEntry *ego_entry;
898 char *name;
899
900 name = NULL;
901
902 if (strlen (GNUNET_REST_API_NS_IDENTITY_NAME) >= strlen (handle->url))
903 {
904 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
905 GNUNET_SCHEDULER_add_now (&do_error, handle);
906 return;
907 }
908 name = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY_NAME) + 1];
909 ego_entry = get_egoentry (handle, NULL, name);
910
911 if (NULL == ego_entry)
912 {
913 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
914 GNUNET_SCHEDULER_add_now (&do_error, handle);
915 return;
916 }
917
918 handle->op = GNUNET_IDENTITY_delete (identity_handle,
919 ego_entry->identifier,
920 &do_finished,
921 handle);
922}
923
924struct ego_sign_data_cls
925{
926 void *data;
927 struct RequestHandle *handle;
928};
929
930void
931ego_sign_data_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego)
932{
933 struct RequestHandle *handle = ((struct ego_sign_data_cls *) cls)->handle;
934 unsigned char *data
935 = (unsigned char *) ((struct ego_sign_data_cls *) cls)->data; // data is url decoded
936 struct MHD_Response *resp;
937 struct GNUNET_CRYPTO_EddsaSignature sig;
938 char *sig_str;
939 char *result;
940
941 if (ego == NULL)
942 {
943 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
944 GNUNET_SCHEDULER_add_now (&do_error, handle);
945 return;
946 }
947
948 if (ntohl (ego->pk.type) != GNUNET_PUBLIC_KEY_TYPE_EDDSA)
949 {
950 handle->ec = GNUNET_EC_IDENTITY_NOT_FOUND;
951 GNUNET_SCHEDULER_add_now (&do_error, handle);
952 return;
953 }
954
955 if ( GNUNET_OK != GNUNET_CRYPTO_eddsa_sign_raw (&(ego->pk.eddsa_key),
956 (void *) data,
957 strlen ( (char*) data),
958 &sig))
959 {
960 handle->ec = GNUNET_EC_UNKNOWN;
961 GNUNET_SCHEDULER_add_now (&do_error, handle);
962 return;
963 }
964
965 GNUNET_STRINGS_base64url_encode (&sig,
966 sizeof (struct GNUNET_CRYPTO_EddsaSignature),
967 &sig_str);
968
969 GNUNET_asprintf (&result,
970 "{\"signature\": \"%s\"}",
971 sig_str);
972
973 resp = GNUNET_REST_create_response (result);
974 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
975
976 free (data);
977 free (sig_str);
978 free (result);
979 free (cls);
980 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
981}
982
983/**
984 *
985 * @param con_handle the connection handle
986 * @param url the url
987 * @param cls the RequestHandle
988 */
989void
990ego_sign_data (struct GNUNET_REST_RequestHandle *con_handle,
991 const char *url,
992 void *cls)
993{
994 // TODO: replace with precompiler #define
995 const char *username_key = "user";
996 const char *data_key = "data";
997
998 struct RequestHandle *handle = cls;
999 struct GNUNET_HashCode cache_key_username;
1000 struct GNUNET_HashCode cache_key_data;
1001 char *username;
1002 char *data;
1003
1004 struct ego_sign_data_cls *cls2;
1005
1006 GNUNET_CRYPTO_hash (username_key, strlen (username_key), &cache_key_username);
1007 GNUNET_CRYPTO_hash (data_key, strlen (data_key), &cache_key_data);
1008
1009 if ((GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1010 handle->rest_handle->url_param_map,
1011 &cache_key_username)) ||
1012 (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (
1013 handle->rest_handle->url_param_map,
1014 &cache_key_data)))
1015 {
1016 handle->ec = GNUNET_EC_UNKNOWN;
1017 GNUNET_SCHEDULER_add_now (&do_error, handle);
1018 return;
1019 }
1020
1021 username = (char *) GNUNET_CONTAINER_multihashmap_get (
1022 handle->rest_handle->url_param_map,
1023 &cache_key_username);
1024
1025 data = (char *) GNUNET_CONTAINER_multihashmap_get (
1026 handle->rest_handle->url_param_map,
1027 &cache_key_data);
1028
1029 cls2 = malloc (sizeof(struct ego_sign_data_cls));
1030 cls2->data = (void *) GNUNET_strdup (data);
1031 cls2->handle = handle;
1032
1033 GNUNET_IDENTITY_ego_lookup (cfg,
1034 username,
1035 ego_sign_data_cb,
1036 cls2);
1037}
1038
1039/**
1040 * Respond to OPTIONS request
1041 *
1042 * @param con_handle the connection handle
1043 * @param url the url
1044 * @param cls the RequestHandle
1045 */
1046static void
1047options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1048 const char *url,
1049 void *cls)
1050{
1051 struct MHD_Response *resp;
1052 struct RequestHandle *handle = cls;
1053
1054 // For now, independent of path return all options
1055 resp = GNUNET_REST_create_response (NULL);
1056 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1057 "Access-Control-Allow-Methods",
1058 allow_methods));
1059 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1060 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
1061 return;
1062}
1063
1064
1065static void
1066list_ego (void *cls,
1067 struct GNUNET_IDENTITY_Ego *ego,
1068 void **ctx,
1069 const char *identifier)
1070{
1071 struct EgoEntry *ego_entry;
1072 struct GNUNET_CRYPTO_PublicKey pk;
1073
1074 if ((NULL == ego) && (ID_REST_STATE_INIT == state))
1075 {
1076 state = ID_REST_STATE_POST_INIT;
1077 return;
1078 }
1079 if (NULL == ego)
1080 {
1081 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1082 "Called with NULL ego\n");
1083 return;
1084 }
1085 if (ID_REST_STATE_INIT == state)
1086 {
1087 ego_entry = GNUNET_new (struct EgoEntry);
1088 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1089 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1090 ego_entry->ego = ego;
1091 ego_entry->identifier = GNUNET_strdup (identifier);
1092 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1093 ego_tail,
1094 ego_entry);
1095 }
1096 /* Ego renamed or added */
1097 if (identifier != NULL)
1098 {
1099 for (ego_entry = ego_head; NULL != ego_entry;
1100 ego_entry = ego_entry->next)
1101 {
1102 if (ego_entry->ego == ego)
1103 {
1104 /* Rename */
1105 GNUNET_free (ego_entry->identifier);
1106 ego_entry->identifier = GNUNET_strdup (identifier);
1107 break;
1108 }
1109 }
1110 if (NULL == ego_entry)
1111 {
1112 /* Add */
1113 ego_entry = GNUNET_new (struct EgoEntry);
1114 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1115 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1116 ego_entry->ego = ego;
1117 ego_entry->identifier = GNUNET_strdup (identifier);
1118 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1119 ego_tail,
1120 ego_entry);
1121 }
1122 }
1123 else
1124 {
1125 /* Delete */
1126 for (ego_entry = ego_head; NULL != ego_entry;
1127 ego_entry = ego_entry->next)
1128 {
1129 if (ego_entry->ego == ego)
1130 break;
1131 }
1132 if (NULL == ego_entry)
1133 return; /* Not found */
1134
1135 GNUNET_CONTAINER_DLL_remove (ego_head,
1136 ego_tail,
1137 ego_entry);
1138 GNUNET_free (ego_entry->identifier);
1139 GNUNET_free (ego_entry->keystring);
1140 GNUNET_free (ego_entry);
1141 return;
1142 }
1143
1144}
1145
1146
1147/**
1148 * Function processing the REST call
1149 *
1150 * @param method HTTP method
1151 * @param url URL of the HTTP request
1152 * @param data body of the HTTP request (optional)
1153 * @param data_size length of the body
1154 * @param proc callback function for the result
1155 * @param proc_cls closure for callback function
1156 * @return GNUNET_OK if request accepted
1157 */
1158static enum GNUNET_GenericReturnValue
1159rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
1160 GNUNET_REST_ResultProcessor proc,
1161 void *proc_cls)
1162{
1163 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1164 struct GNUNET_REST_RequestHandlerError err;
1165 static const struct GNUNET_REST_RequestHandler handlers[] =
1166 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_PUBKEY,
1167 &ego_get_pubkey },
1168 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_NAME, &ego_get_name },
1169 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_get_all },
1170 { MHD_HTTP_METHOD_PUT,
1171 GNUNET_REST_API_NS_IDENTITY_PUBKEY,
1172 &ego_edit_pubkey },
1173 { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY_NAME, &ego_edit_name },
1174 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY, &ego_create },
1175 { MHD_HTTP_METHOD_DELETE,
1176 GNUNET_REST_API_NS_IDENTITY_PUBKEY,
1177 &ego_delete_pubkey },
1178 { MHD_HTTP_METHOD_DELETE,
1179 GNUNET_REST_API_NS_IDENTITY_NAME,
1180 &ego_delete_name },
1181 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY, &options_cont },
1182 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_SIGN, &ego_sign_data},
1183 GNUNET_REST_HANDLER_END };
1184
1185
1186 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1187 handle->proc_cls = proc_cls;
1188 handle->proc = proc;
1189 handle->rest_handle = rest_handle;
1190 handle->data = rest_handle->data;
1191 handle->data_size = rest_handle->data_size;
1192
1193 handle->url = GNUNET_strdup (rest_handle->url);
1194 if (handle->url[strlen (handle->url) - 1] == '/')
1195 handle->url[strlen (handle->url) - 1] = '\0';
1196 handle->timeout_task =
1197 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
1198 GNUNET_CONTAINER_DLL_insert (requests_head,
1199 requests_tail,
1200 handle);
1201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
1202 if (GNUNET_NO ==
1203 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
1204 {
1205 cleanup_handle (handle);
1206 return GNUNET_NO;
1207 }
1208
1209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
1210 return GNUNET_YES;
1211}
1212
1213
1214/**
1215 * Entry point for the plugin.
1216 *
1217 * @param cls Config info
1218 * @return NULL on error, otherwise the plugin context
1219 */
1220void *
1221libgnunet_plugin_rest_identity_init (void *cls)
1222{
1223 static struct Plugin plugin;
1224 struct GNUNET_REST_Plugin *api;
1225
1226 cfg = cls;
1227 if (NULL != plugin.cfg)
1228 return NULL; /* can only initialize once! */
1229 memset (&plugin, 0, sizeof(struct Plugin));
1230 plugin.cfg = cfg;
1231 api = GNUNET_new (struct GNUNET_REST_Plugin);
1232 api->cls = &plugin;
1233 api->name = GNUNET_REST_API_NS_IDENTITY;
1234 api->process_request = &rest_process_request;
1235 GNUNET_asprintf (&allow_methods,
1236 "%s, %s, %s, %s, %s",
1237 MHD_HTTP_METHOD_GET,
1238 MHD_HTTP_METHOD_POST,
1239 MHD_HTTP_METHOD_PUT,
1240 MHD_HTTP_METHOD_DELETE,
1241 MHD_HTTP_METHOD_OPTIONS);
1242 state = ID_REST_STATE_INIT;
1243 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
1244
1245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Identity REST API initialized\n"));
1246 return api;
1247}
1248
1249
1250/**
1251 * Exit point from the plugin.
1252 *
1253 * @param cls the plugin context (as returned by "init")
1254 * @return always NULL
1255 */
1256void *
1257libgnunet_plugin_rest_identity_done (void *cls)
1258{
1259 struct GNUNET_REST_Plugin *api = cls;
1260 struct Plugin *plugin = api->cls;
1261 struct EgoEntry *ego_entry;
1262 struct EgoEntry *ego_tmp;
1263
1264 plugin->cfg = NULL;
1265 while (NULL != requests_head)
1266 cleanup_handle (requests_head);
1267 if (NULL != identity_handle)
1268 GNUNET_IDENTITY_disconnect (identity_handle);
1269
1270 for (ego_entry = ego_head; NULL != ego_entry;)
1271 {
1272 ego_tmp = ego_entry;
1273 ego_entry = ego_entry->next;
1274 GNUNET_free (ego_tmp->identifier);
1275 GNUNET_free (ego_tmp->keystring);
1276 GNUNET_free (ego_tmp);
1277 }
1278
1279 GNUNET_free (allow_methods);
1280 GNUNET_free (api);
1281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Identity REST plugin is finished\n");
1282 return NULL;
1283}
1284
1285
1286/* end of plugin_rest_identity.c */
diff --git a/src/rest-plugin/meson.build b/src/rest-plugin/meson.build
deleted file mode 100644
index 31d49864e..000000000
--- a/src/rest-plugin/meson.build
+++ /dev/null
@@ -1,6 +0,0 @@
1if get_option('monolith') == false
2 subdir('rest')
3endif
4subdir('identity')
5subdir('gns')
6subdir('reclaim')
diff --git a/src/rest-plugin/reclaim/Makefile.am b/src/rest-plugin/reclaim/Makefile.am
deleted file mode 100644
index 7bc305bef..000000000
--- a/src/rest-plugin/reclaim/Makefile.am
+++ /dev/null
@@ -1,81 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4 plugindir = $(libdir)/gnunet
5
6if USE_COVERAGE
7 AM_CFLAGS = --coverage -O0
8 XLIB = -lgcov
9endif
10
11
12REST_PLUGIN = \
13 libgnunet_plugin_rest_reclaim.la
14
15if HAVE_JOSE
16REST_PLUGIN += libgnunet_plugin_rest_openid_connect.la
17endif
18
19if HAVE_PABC
20 REST_PLUGIN += libgnunet_plugin_rest_pabc.la
21endif
22
23pkgcfgdir= $(pkgdatadir)/config.d/
24
25libexecdir= $(pkglibdir)/libexec/
26
27plugin_LTLIBRARIES = \
28 $(REST_PLUGIN)
29
30libgnunet_plugin_rest_reclaim_la_SOURCES = \
31 plugin_rest_reclaim.c \
32 json_reclaim.h \
33 json_reclaim.c
34libgnunet_plugin_rest_reclaim_la_LIBADD = \
35 $(top_builddir)/src/service/identity/libgnunetidentity.la \
36 $(top_builddir)/src/service/reclaim/libgnunetreclaim.la \
37 $(top_builddir)/src/lib/json/libgnunetjson.la \
38 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
39 $(top_builddir)/src/service/rest/libgnunetrest.la \
40 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
41 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
42 $(LTLIBINTL) -ljansson $(MHD_LIBS)
43libgnunet_plugin_rest_reclaim_la_LDFLAGS = \
44 $(GN_PLUGIN_LDFLAGS)
45libgnunet_plugin_rest_reclaim_la_CFLAGS = \
46 $(MHD_CFLAGS) $(AM_CFLAGS)
47
48if HAVE_JOSE
49libgnunet_plugin_rest_openid_connect_la_SOURCES = \
50 plugin_rest_openid_connect.c \
51 oidc_helper.h \
52 oidc_helper.c
53libgnunet_plugin_rest_openid_connect_la_LIBADD = \
54 $(top_builddir)/src/service/identity/libgnunetidentity.la \
55 $(top_builddir)/src/service/reclaim/libgnunetreclaim.la \
56 $(top_builddir)/src/service/rest/libgnunetrest.la \
57 $(top_builddir)/src/service/namestore/libgnunetnamestore.la \
58 $(top_builddir)/src/service/gns/libgnunetgns.la \
59 $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \
60 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
61 $(LTLIBINTL) -ljansson -ljose $(MHD_LIBS) \
62 $(LIBGCRYPT_LIBS)
63libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \
64 $(GN_PLUGIN_LDFLAGS)
65libgnunet_plugin_rest_openid_connect_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
66endif
67
68if HAVE_PABC
69libgnunet_plugin_rest_pabc_la_SOURCES = \
70 plugin_rest_pabc.c \
71 $(top_builddir)/src/plugin/reclaim/pabc_helper.c
72libgnunet_plugin_rest_pabc_la_LIBADD = \
73 $(top_builddir)/src/service/reclaim/libgnunetreclaim.la \
74 $(top_builddir)/src/lib/json/libgnunetjson.la \
75 $(top_builddir)/src/service/rest/libgnunetrest.la \
76 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
77 $(LTLIBINTL) -ljansson -lpabc $(MHD_LIBS)
78libgnunet_plugin_rest_pabc_la_LDFLAGS = \
79 $(GN_PLUGIN_LDFLAGS)
80libgnunet_plugin_rest_pabc_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
81endif
diff --git a/src/rest-plugin/reclaim/json_reclaim.c b/src/rest-plugin/reclaim/json_reclaim.c
deleted file mode 100644
index b1ca7a4a5..000000000
--- a/src/rest-plugin/reclaim/json_reclaim.c
+++ /dev/null
@@ -1,398 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/**
22 * @file rest-plugins/json_reclaim.c
23 * @brief JSON handling of reclaim data
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_reclaim_lib.h"
30#include "gnunet_reclaim_service.h"
31
32
33/**
34 * Parse given JSON object to a claim
35 *
36 * @param cls closure, NULL
37 * @param root the json object representing data
38 * @param spec where to write the data
39 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
40 */
41static int
42parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec)
43{
44 struct GNUNET_RECLAIM_Attribute *attr;
45 const char *name_str = NULL;
46 const char *val_str = NULL;
47 const char *type_str = NULL;
48 const char *id_str = NULL;
49 const char *cred_str = NULL;
50 const char *flag_str = NULL;
51 char *data;
52 int unpack_state;
53 uint32_t type;
54 size_t data_size;
55
56 GNUNET_assert (NULL != root);
57
58 if (! json_is_object (root))
59 {
60 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
61 "Error json is not array nor object!\n");
62 return GNUNET_SYSERR;
63 }
64 // interpret single attribute
65 unpack_state = json_unpack (root,
66 "{s:s, s?s, s?s, s:s, s:s, s?s!}",
67 "name",
68 &name_str,
69 "id",
70 &id_str,
71 "credential",
72 &cred_str,
73 "type",
74 &type_str,
75 "value",
76 &val_str,
77 "flag",
78 &flag_str);
79 if ((0 != unpack_state) || (NULL == name_str) || (NULL == val_str) ||
80 (NULL == type_str))
81 {
82 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
83 "Error json object has a wrong format!\n");
84 return GNUNET_SYSERR;
85 }
86 type = GNUNET_RECLAIM_attribute_typename_to_number (type_str);
87 if (GNUNET_SYSERR ==
88 (GNUNET_RECLAIM_attribute_string_to_value (type,
89 val_str,
90 (void **) &data,
91 &data_size)))
92 {
93 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Attribute value invalid!\n");
94 return GNUNET_SYSERR;
95 }
96 attr = GNUNET_RECLAIM_attribute_new (name_str, NULL,
97 type, data, data_size);
98 GNUNET_free (data);
99 if ((NULL != cred_str) && (0 != strlen (cred_str)))
100 {
101 GNUNET_STRINGS_string_to_data (cred_str,
102 strlen (cred_str),
103 &attr->credential,
104 sizeof(attr->credential));
105 }
106 if ((NULL == id_str) || (0 == strlen (id_str)))
107 memset (&attr->id, 0, sizeof (attr->id));
108 else
109 GNUNET_STRINGS_string_to_data (id_str,
110 strlen (id_str),
111 &attr->id,
112 sizeof(attr->id));
113
114 *(struct GNUNET_RECLAIM_Attribute **) spec->ptr = attr;
115 return GNUNET_OK;
116}
117
118
119/**
120 * Cleanup data left from parsing RSA public key.
121 *
122 * @param cls closure, NULL
123 * @param[out] spec where to free the data
124 */
125static void
126clean_attr (void *cls, struct GNUNET_JSON_Specification *spec)
127{
128 struct GNUNET_RECLAIM_Attribute **attr;
129
130 attr = (struct GNUNET_RECLAIM_Attribute **) spec->ptr;
131 if (NULL != *attr)
132 {
133 GNUNET_free (*attr);
134 *attr = NULL;
135 }
136}
137
138
139/**
140 * JSON Specification for Reclaim claims.
141 *
142 * @param ticket struct of GNUNET_RECLAIM_Attribute to fill
143 * @return JSON Specification
144 */
145struct GNUNET_JSON_Specification
146GNUNET_RECLAIM_JSON_spec_attribute (struct GNUNET_RECLAIM_Attribute **attr)
147{
148 struct GNUNET_JSON_Specification ret = { .parser = &parse_attr,
149 .cleaner = &clean_attr,
150 .cls = NULL,
151 .field = NULL,
152 .ptr = attr,
153 .ptr_size = 0,
154 .size_ptr = NULL };
155
156 *attr = NULL;
157 return ret;
158}
159
160
161/**
162 * Parse given JSON object to a ticket
163 *
164 * @param cls closure, NULL
165 * @param root the json object representing data
166 * @param spec where to write the data
167 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
168 */
169static int
170parse_ticket (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec)
171{
172 struct GNUNET_RECLAIM_Ticket *ticket;
173 const char *rnd_str;
174 const char *aud_str;
175 const char *id_str;
176 int unpack_state;
177
178 GNUNET_assert (NULL != root);
179
180 if (! json_is_object (root))
181 {
182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183 "Error json is not array nor object!\n");
184 return GNUNET_SYSERR;
185 }
186 // interpret single ticket
187 unpack_state = json_unpack (root,
188 "{s:s, s:s, s:s!}",
189 "rnd",
190 &rnd_str,
191 "audience",
192 &aud_str,
193 "issuer",
194 &id_str);
195 if (0 != unpack_state)
196 {
197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
198 "Error json object has a wrong format!\n");
199 return GNUNET_SYSERR;
200 }
201 ticket = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
202 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (rnd_str,
203 strlen (rnd_str),
204 &ticket->rnd,
205 sizeof(ticket->rnd)))
206 {
207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Rnd invalid\n");
208 GNUNET_free (ticket);
209 return GNUNET_SYSERR;
210 }
211 if (GNUNET_OK !=
212 GNUNET_STRINGS_string_to_data (id_str,
213 strlen (id_str),
214 &ticket->identity,
215 sizeof(ticket->identity)))
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Identity invalid\n");
218 GNUNET_free (ticket);
219 return GNUNET_SYSERR;
220 }
221
222 if (GNUNET_OK !=
223 GNUNET_STRINGS_string_to_data (aud_str,
224 strlen (aud_str),
225 &ticket->audience,
226 sizeof(ticket->audience)))
227 {
228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Audience invalid\n");
229 GNUNET_free (ticket);
230 return GNUNET_SYSERR;
231 }
232
233 *(struct GNUNET_RECLAIM_Ticket **) spec->ptr = ticket;
234 return GNUNET_OK;
235}
236
237
238/**
239 * Cleanup data left from parsing RSA public key.
240 *
241 * @param cls closure, NULL
242 * @param[out] spec where to free the data
243 */
244static void
245clean_ticket (void *cls, struct GNUNET_JSON_Specification *spec)
246{
247 struct GNUNET_RECLAIM_Ticket **ticket;
248
249 ticket = (struct GNUNET_RECLAIM_Ticket **) spec->ptr;
250 if (NULL != *ticket)
251 {
252 GNUNET_free (*ticket);
253 *ticket = NULL;
254 }
255}
256
257
258/**
259 * JSON Specification for Reclaim tickets.
260 *
261 * @param ticket struct of GNUNET_RECLAIM_Ticket to fill
262 * @return JSON Specification
263 */
264struct GNUNET_JSON_Specification
265GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket)
266{
267 struct GNUNET_JSON_Specification ret = { .parser = &parse_ticket,
268 .cleaner = &clean_ticket,
269 .cls = NULL,
270 .field = NULL,
271 .ptr = ticket,
272 .ptr_size = 0,
273 .size_ptr = NULL };
274
275 *ticket = NULL;
276 return ret;
277}
278
279
280/**
281 * Parse given JSON object to a credential claim
282 *
283 * @param cls closure, NULL
284 * @param root the json object representing data
285 * @param spec where to write the data
286 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
287 */
288static int
289parse_credential (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec)
290{
291 struct GNUNET_RECLAIM_Credential *cred;
292 const char *name_str = NULL;
293 const char *type_str = NULL;
294 const char *id_str = NULL;
295 json_t *val_json;
296 char *data = NULL;
297 char *val_str = NULL;
298 int unpack_state;
299 uint32_t type;
300 size_t data_size;
301
302 GNUNET_assert (NULL != root);
303
304 if (! json_is_object (root))
305 {
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307 "Error json is not array nor object!\n");
308 return GNUNET_SYSERR;
309 }
310 // interpret single attribute
311 unpack_state = json_unpack (root,
312 "{s:s, s?s, s:s, s:o!}",
313 "name",
314 &name_str,
315 "id",
316 &id_str,
317 "type",
318 &type_str,
319 "value",
320 &val_json);
321 if ((0 != unpack_state) || (NULL == name_str) || (NULL == val_json) ||
322 (NULL == type_str))
323 {
324 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
325 "Error json object has a wrong format!\n");
326 return GNUNET_SYSERR;
327 }
328 if (json_is_string (val_json)) {
329 val_str = GNUNET_strdup (json_string_value (val_json));
330 } else {
331 val_str = json_dumps (val_json, JSON_COMPACT);
332 }
333 type = GNUNET_RECLAIM_credential_typename_to_number (type_str);
334 if (GNUNET_SYSERR ==
335 (GNUNET_RECLAIM_credential_string_to_value (type,
336 val_str,
337 (void **) &data,
338 &data_size)))
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Credential value invalid!\n");
341 return GNUNET_SYSERR;
342 }
343 cred = GNUNET_RECLAIM_credential_new (name_str, type, data, data_size);
344 GNUNET_free (data);
345 if ((NULL == id_str) || (0 == strlen (id_str)))
346 memset (&cred->id, 0, sizeof (cred->id));
347 else
348 GNUNET_STRINGS_string_to_data (id_str,
349 strlen (id_str),
350 &cred->id,
351 sizeof(cred->id));
352
353 *(struct GNUNET_RECLAIM_Credential **) spec->ptr = cred;
354 return GNUNET_OK;
355}
356
357
358/**
359 * Cleanup data left from parsing RSA public key.
360 *
361 * @param cls closure, NULL
362 * @param[out] spec where to free the data
363 */
364static void
365clean_credential (void *cls, struct GNUNET_JSON_Specification *spec)
366{
367 struct GNUNET_RECLAIM_Credential **attr;
368
369 attr = (struct GNUNET_RECLAIM_Credential **) spec->ptr;
370 if (NULL != *attr)
371 {
372 GNUNET_free (*attr);
373 *attr = NULL;
374 }
375}
376
377
378/**
379 * JSON Specification for credential claims.
380 *
381 * @param attr struct of GNUNET_RECLAIM_Credential to fill
382 * @return JSON Specification
383 */
384struct GNUNET_JSON_Specification
385GNUNET_RECLAIM_JSON_spec_credential (struct
386 GNUNET_RECLAIM_Credential **cred)
387{
388 struct GNUNET_JSON_Specification ret = { .parser = &parse_credential,
389 .cleaner = &clean_credential,
390 .cls = NULL,
391 .field = NULL,
392 .ptr = cred,
393 .ptr_size = 0,
394 .size_ptr = NULL };
395
396 *cred = NULL;
397 return ret;
398}
diff --git a/src/rest-plugin/reclaim/json_reclaim.h b/src/rest-plugin/reclaim/json_reclaim.h
deleted file mode 100644
index 613ddf873..000000000
--- a/src/rest-plugin/reclaim/json_reclaim.h
+++ /dev/null
@@ -1,57 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/**
22 * @file rest-plugins/json_reclaim.h
23 * @brief JSON handling of reclaim data
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_reclaim_service.h"
30#include "gnunet_reclaim_lib.h"
31
32/**
33 * JSON Specification for Reclaim claims.
34 *
35 * @param attr struct of GNUNET_RECLAIM_Attribute to fill
36 * @return JSON Specification
37 */
38struct GNUNET_JSON_Specification
39GNUNET_RECLAIM_JSON_spec_attribute (struct GNUNET_RECLAIM_Attribute **attr);
40
41/**
42 * JSON Specification for Reclaim tickets.
43 *
44 * @param ticket struct of GNUNET_RECLAIM_Ticket to fill
45 * @return JSON Specification
46 */
47struct GNUNET_JSON_Specification
48GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket);
49
50/**
51 * JSON Specification for credentials.
52 *
53 * @param cred struct of GNUNET_RECLAIM_Credential to fill
54 * @return JSON Specification
55 */
56struct GNUNET_JSON_Specification
57GNUNET_RECLAIM_JSON_spec_credential (struct GNUNET_RECLAIM_Credential **cred);
diff --git a/src/rest-plugin/reclaim/meson.build b/src/rest-plugin/reclaim/meson.build
deleted file mode 100644
index da4406850..000000000
--- a/src/rest-plugin/reclaim/meson.build
+++ /dev/null
@@ -1,29 +0,0 @@
1shared_module('gnunet_plugin_rest_reclaim',
2 ['plugin_rest_reclaim.c', 'json_reclaim.c'],
3 dependencies: [libgnunetrest_dep,
4 libgnunetidentity_dep,
5 libgnunetreclaim_dep,
6 libgnunetjson_dep,
7 libgnunetutil_dep,
8 json_dep,
9 mhd_dep],
10 include_directories: [incdir, configuration_inc],
11 install: true,
12 install_dir: get_option('libdir') / 'gnunet')
13
14shared_module('gnunet_plugin_rest_openid_connect',
15 ['plugin_rest_openid_connect.c', 'oidc_helper.c'],
16 dependencies: [libgnunetrest_dep,
17 libgnunetreclaim_dep,
18 libgnunetidentity_dep,
19 libgnunetgns_dep,
20 libgnunetutil_dep,
21 jose_dep,
22 gcrypt_dep,
23 json_dep,
24 mhd_dep],
25 include_directories: [incdir, configuration_inc],
26 install: true,
27 install_dir: get_option('libdir') / 'gnunet')
28
29
diff --git a/src/rest-plugin/reclaim/oidc_helper.c b/src/rest-plugin/reclaim/oidc_helper.c
deleted file mode 100644
index a2da7312b..000000000
--- a/src/rest-plugin/reclaim/oidc_helper.c
+++ /dev/null
@@ -1,1026 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file reclaim/oidc_helper.c
23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach
25 * @author Tristan Schwieren
26 */
27#include "platform.h"
28#include <inttypes.h>
29#include <jansson.h>
30#include <jose/jose.h>
31#include "gnunet_util_lib.h"
32#include "gnunet_reclaim_lib.h"
33#include "gnunet_reclaim_service.h"
34#include "gnunet_signatures.h"
35#include "oidc_helper.h"
36// #include "benchmark.h"
37#include <gcrypt.h>
38
39GNUNET_NETWORK_STRUCT_BEGIN
40
41/**
42 * The signature used to generate the authorization code
43 */
44struct OIDC_Parameters
45{
46 /**
47 * The reclaim ticket
48 */
49 struct GNUNET_RECLAIM_Ticket ticket;
50
51 /**
52 * The nonce length
53 */
54 uint32_t nonce_len GNUNET_PACKED;
55
56 /**
57 * The length of the PKCE code_challenge
58 */
59 uint32_t code_challenge_len GNUNET_PACKED;
60
61 /**
62 * The length of the attributes list
63 */
64 uint32_t attr_list_len GNUNET_PACKED;
65
66 /**
67 * The length of the presentation list
68 */
69 uint32_t pres_list_len GNUNET_PACKED;
70};
71
72GNUNET_NETWORK_STRUCT_END
73
74/**
75 * Standard claims represented by the "profile" scope in OIDC
76 */
77static char OIDC_profile_claims[14][32] = {
78 "name", "family_name", "given_name", "middle_name", "nickname",
79 "preferred_username", "profile", "picture", "website", "gender", "birthdate",
80 "zoneinfo", "locale", "updated_at"
81};
82
83/**
84 * Standard claims represented by the "email" scope in OIDC
85 */
86static char OIDC_email_claims[2][16] = {
87 "email", "email_verified"
88};
89
90/**
91 * Standard claims represented by the "phone" scope in OIDC
92 */
93static char OIDC_phone_claims[2][32] = {
94 "phone_number", "phone_number_verified"
95};
96
97/**
98 * Standard claims represented by the "address" scope in OIDC
99 */
100static char OIDC_address_claims[5][32] = {
101 "street_address", "locality", "region", "postal_code", "country"
102};
103
104static enum GNUNET_GenericReturnValue
105is_claim_in_address_scope (const char *claim)
106{
107 int i;
108 for (i = 0; i < 5; i++)
109 {
110 if (0 == strcmp (claim, OIDC_address_claims[i]))
111 {
112 return GNUNET_YES;
113 }
114 }
115 return GNUNET_NO;
116}
117
118
119static char *
120create_jwt_hmac_header (void)
121{
122 json_t *root;
123 char *json_str;
124
125 root = json_object ();
126 json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE_HMAC));
127 json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
128
129 json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT);
130 json_decref (root);
131 return json_str;
132}
133
134
135static void
136replace_char (char *str, char find, char replace)
137{
138 char *current_pos = strchr (str, find);
139
140 while (current_pos)
141 {
142 *current_pos = replace;
143 current_pos = strchr (current_pos, find);
144 }
145}
146
147
148// RFC4648
149static void
150fix_base64 (char *str)
151{
152 // Replace + with -
153 replace_char (str, '+', '-');
154
155 // Replace / with _
156 replace_char (str, '/', '_');
157}
158
159
160static json_t*
161generate_userinfo_json (const struct GNUNET_CRYPTO_PublicKey *sub_key,
162 const struct GNUNET_RECLAIM_AttributeList *attrs,
163 const struct
164 GNUNET_RECLAIM_PresentationList *presentations)
165{
166 struct GNUNET_RECLAIM_AttributeListEntry *le;
167 struct GNUNET_RECLAIM_PresentationListEntry *ple;
168 char *subject;
169 char *source_name;
170 char *attr_val_str;
171 char *pres_val_str;
172 json_t *body;
173 json_t *aggr_names;
174 json_t *aggr_sources;
175 json_t *aggr_sources_jwt;
176 json_t *addr_claim = NULL;
177 int num_presentations = 0;
178 for (le = attrs->list_head; NULL != le; le = le->next)
179 {
180 if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential))
181 num_presentations++;
182 }
183
184 subject =
185 GNUNET_STRINGS_data_to_string_alloc (sub_key,
186 sizeof(struct
187 GNUNET_CRYPTO_PublicKey));
188 body = json_object ();
189 aggr_names = json_object ();
190 aggr_sources = json_object ();
191
192 // iss REQUIRED case sensitive server uri with https
193 // The issuer is the local reclaim instance (e.g.
194 // https://reclaim.id/api/openid)
195 json_object_set_new (body, "iss", json_string (SERVER_ADDRESS));
196 // sub REQUIRED public key identity, not exceed 255 ASCII length
197 json_object_set_new (body, "sub", json_string (subject));
198 GNUNET_free (subject);
199 pres_val_str = NULL;
200 source_name = NULL;
201 int i = 0;
202 for (ple = presentations->list_head; NULL != ple; ple = ple->next)
203 {
204 // New presentation
205 GNUNET_asprintf (&source_name,
206 "src%d",
207 i);
208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
209 "Adding new presentation source #%d\n", i);
210 aggr_sources_jwt = json_object ();
211 pres_val_str =
212 GNUNET_RECLAIM_presentation_value_to_string (ple->presentation->type,
213 ple->presentation->data,
214 ple->presentation->data_size);
215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
216 "Presentation is: %s\n", pres_val_str);
217 json_object_set_new (aggr_sources_jwt,
218 GNUNET_RECLAIM_presentation_number_to_typename (
219 ple->presentation->type),
220 json_string (pres_val_str) );
221 json_object_set_new (aggr_sources, source_name, aggr_sources_jwt);
222 GNUNET_free (pres_val_str);
223 GNUNET_free (source_name);
224 source_name = NULL;
225 i++;
226 }
227
228 int addr_is_aggregated = GNUNET_NO;
229 int addr_is_normal = GNUNET_NO;
230 for (le = attrs->list_head; NULL != le; le = le->next)
231 {
232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
233 "Processing %s for userinfo body\n",
234 le->attribute->name);
235 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential))
236 {
237 attr_val_str =
238 GNUNET_RECLAIM_attribute_value_to_string (le->attribute->type,
239 le->attribute->data,
240 le->attribute->data_size);
241 /**
242 * There is this weird quirk that the individual address claim(s) must be
243 * inside a JSON object of the "address" claim.
244 */
245 if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name))
246 {
247 if (GNUNET_YES == addr_is_aggregated)
248 {
249 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
250 "Address is set as aggregated claim. Skipping self-issued value...\n");
251 GNUNET_free (attr_val_str);
252 continue;
253 }
254 addr_is_normal = GNUNET_YES;
255
256 if (NULL == addr_claim)
257 {
258 addr_claim = json_object ();
259 json_object_set_new (body, "address", addr_claim);
260 }
261 json_object_set_new (addr_claim, le->attribute->name,
262 json_string (attr_val_str));
263
264 }
265 else
266 {
267 json_object_set_new (body, le->attribute->name,
268 json_string (attr_val_str));
269 }
270 GNUNET_free (attr_val_str);
271 }
272 else
273 {
274 // Check if presentation is there
275 int j = 0;
276 for (ple = presentations->list_head; NULL != ple; ple = ple->next)
277 {
278 if (GNUNET_YES ==
279 GNUNET_RECLAIM_id_is_equal (&ple->presentation->credential_id,
280 &le->attribute->credential))
281 break;
282 j++;
283 }
284 if (NULL == ple)
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
287 "Presentation for `%s' missing...\n",
288 le->attribute->name);
289 continue;
290 }
291 /**
292 * There is this weird quirk that the individual address claim(s) must be
293 * inside a JSON object of the "address" claim.
294 */
295 if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name))
296 {
297 if (GNUNET_YES == addr_is_normal)
298 {
299 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
300 "Address is already set as normal claim. Skipping attested value...\n");
301 continue;
302 }
303 addr_is_aggregated = GNUNET_YES;
304 /** This is/can only be set once! **/
305 if (NULL != addr_claim)
306 continue;
307 addr_claim = json_object ();
308 GNUNET_asprintf (&source_name,
309 "src%d",
310 j);
311 json_object_set_new (aggr_names, "address",
312 json_string (source_name));
313 GNUNET_free (source_name);
314 }
315 else
316 {
317 // Presentation exists, hence take the respective source str
318 GNUNET_asprintf (&source_name,
319 "src%d",
320 j);
321 json_object_set_new (aggr_names, le->attribute->name,
322 json_string (source_name));
323 GNUNET_free (source_name);
324 }
325 }
326 }
327 if (0 != i)
328 {
329 json_object_set_new (body, "_claim_names", aggr_names);
330 json_object_set_new (body, "_claim_sources", aggr_sources);
331 }
332
333 return body;
334}
335
336
337/**
338 * Generate userinfo JSON as string
339 *
340 * @param sub_key the subject (user)
341 * @param attrs user attribute list
342 * @param presentations credential presentation list (may be empty)
343 * @return Userinfo JSON
344 */
345char *
346OIDC_generate_userinfo (const struct GNUNET_CRYPTO_PublicKey *sub_key,
347 const struct GNUNET_RECLAIM_AttributeList *attrs,
348 const struct
349 GNUNET_RECLAIM_PresentationList *presentations)
350{
351 char *body_str;
352 json_t*body = generate_userinfo_json (sub_key,
353 attrs,
354 presentations);
355 body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT);
356 json_decref (body);
357 return body_str;
358}
359
360
361char *
362generate_id_token_body (const struct GNUNET_CRYPTO_PublicKey *aud_key,
363 const struct GNUNET_CRYPTO_PublicKey *sub_key,
364 const struct GNUNET_RECLAIM_AttributeList *attrs,
365 const struct
366 GNUNET_RECLAIM_PresentationList *presentations,
367 const struct GNUNET_TIME_Relative *expiration_time,
368 const char *nonce)
369{
370 struct GNUNET_TIME_Absolute exp_time;
371 struct GNUNET_TIME_Absolute time_now;
372 json_t *body;
373 char *audience;
374 char *subject;
375 char *body_str;
376
377 body = generate_userinfo_json (sub_key,
378 attrs,
379 presentations);
380 // iat REQUIRED time now
381 time_now = GNUNET_TIME_absolute_get ();
382 // exp REQUIRED time expired from config
383 exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time);
384 // auth_time only if max_age
385 // nonce only if nonce
386 // OPTIONAL acr,amr,azp
387 subject =
388 GNUNET_STRINGS_data_to_string_alloc (sub_key,
389 sizeof(struct
390 GNUNET_CRYPTO_PublicKey));
391 audience =
392 GNUNET_STRINGS_data_to_string_alloc (aud_key,
393 sizeof(struct
394 GNUNET_CRYPTO_PublicKey));
395
396 // aud REQUIRED public key client_id must be there
397 json_object_set_new (body, "aud", json_string (audience));
398 // iat
399 json_object_set_new (body,
400 "iat",
401 json_integer (time_now.abs_value_us / (1000 * 1000)));
402 // exp
403 json_object_set_new (body,
404 "exp",
405 json_integer (exp_time.abs_value_us / (1000 * 1000)));
406 // nbf
407 json_object_set_new (body,
408 "nbf",
409 json_integer (time_now.abs_value_us / (1000 * 1000)));
410 // nonce
411 if (NULL != nonce)
412 json_object_set_new (body, "nonce", json_string (nonce));
413
414 // Error checking
415 body_str = json_dumps (body, JSON_INDENT (2) | JSON_COMPACT);
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str);
417
418 json_decref (body);
419 GNUNET_free (subject);
420 GNUNET_free (audience);
421
422 return body_str;
423}
424
425
426char *
427OIDC_generate_id_token_rsa (const struct GNUNET_CRYPTO_PublicKey *aud_key,
428 const struct GNUNET_CRYPTO_PublicKey *sub_key,
429 const struct GNUNET_RECLAIM_AttributeList *attrs,
430 const struct
431 GNUNET_RECLAIM_PresentationList *presentations,
432 const struct GNUNET_TIME_Relative *expiration_time,
433 const char *nonce,
434 const json_t *secret_rsa_key)
435{
436 json_t *jws;
437 char *body_str;
438 char *result;
439
440 // Generate the body of the JSON Web Signature
441 body_str = generate_id_token_body (aud_key,
442 sub_key,
443 attrs,
444 presentations,
445 expiration_time,
446 nonce);
447
448 if (NULL == body_str)
449 {
450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
451 "Body for the JWS could not be generated\n");
452 return NULL;
453 }
454
455 // Creating the JSON Web Signature.
456 jws = json_pack ("{s:o}", "payload",
457 jose_b64_enc (body_str, strlen (body_str)));
458 GNUNET_free (body_str);
459
460 if (! jose_jws_sig (NULL, jws, NULL, secret_rsa_key))
461 {
462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463 "Signature generation failed\n");
464 return NULL;
465 }
466
467 // Encoding JSON as compact JSON Web Signature
468 GNUNET_asprintf (&result, "%s.%s.%s",
469 json_string_value (json_object_get (jws, "protected")),
470 json_string_value (json_object_get (jws, "payload")),
471 json_string_value (json_object_get (jws, "signature")) );
472
473 json_decref (jws);
474 return result;
475}
476
477/**
478 * Create a JWT using HMAC (HS256) from attributes
479 *
480 * @param aud_key the public of the audience
481 * @param sub_key the public key of the subject
482 * @param attrs the attribute list
483 * @param presentations credential presentation list (may be empty)
484 * @param expiration_time the validity of the token
485 * @param secret_key the key used to sign the JWT
486 * @return a new base64-encoded JWT string.
487 */
488char *
489OIDC_generate_id_token_hmac (const struct GNUNET_CRYPTO_PublicKey *aud_key,
490 const struct GNUNET_CRYPTO_PublicKey *sub_key,
491 const struct GNUNET_RECLAIM_AttributeList *attrs,
492 const struct
493 GNUNET_RECLAIM_PresentationList *presentations,
494 const struct GNUNET_TIME_Relative *expiration_time,
495 const char *nonce,
496 const char *secret_key)
497{
498 struct GNUNET_HashCode signature;
499 char *header;
500 char *header_base64;
501 char *body_str;
502 char *body_base64;
503 char *signature_target;
504 char *signature_base64;
505 char *result;
506
507 // Generate and encode Header
508 header = create_jwt_hmac_header ();
509 if (NULL == header)
510 {
511 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
512 "Header for the JWS could not be generated\n");
513 return NULL;
514 }
515 GNUNET_STRINGS_base64url_encode (header, strlen (header), &header_base64);
516 GNUNET_free (header);
517 fix_base64 (header_base64);
518
519 // Generate and encode the body of the JSON Web Signature
520 body_str = generate_id_token_body (aud_key,
521 sub_key,
522 attrs,
523 presentations,
524 expiration_time,
525 nonce);
526
527 if (NULL == body_str)
528 {
529 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
530 "Body for the JWS could not be generated\n");
531 GNUNET_free (header_base64);
532 return NULL;
533 }
534
535 GNUNET_STRINGS_base64url_encode (body_str, strlen (body_str), &body_base64);
536 fix_base64 (body_base64);
537
538 /**
539 * Creating the JWT signature. This might not be
540 * standards compliant, check.
541 */
542 GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64);
543 GNUNET_CRYPTO_hmac_raw (secret_key,
544 strlen (secret_key),
545 signature_target,
546 strlen (signature_target),
547 &signature);
548 GNUNET_STRINGS_base64url_encode ((const char *) &signature,
549 sizeof(struct GNUNET_HashCode),
550 &signature_base64);
551 fix_base64 (signature_base64);
552
553 GNUNET_asprintf (&result,
554 "%s.%s.%s",
555 header_base64,
556 body_base64,
557 signature_base64);
558
559 GNUNET_free (header_base64);
560 GNUNET_free (body_str);
561 GNUNET_free (body_base64);
562 GNUNET_free (signature_target);
563 GNUNET_free (signature_base64);
564 return result;
565}
566
567
568/**
569 * Builds an OIDC authorization code including
570 * a reclaim ticket and nonce
571 *
572 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
573 * @param ticket the ticket to include in the code
574 * @param attrs list of attributes which are shared
575 * @param presentations credential presentation list (may be empty)
576 * @param nonce the nonce to include in the code
577 * @param code_challenge PKCE code challenge
578 * @return a new authorization code (caller must free)
579 */
580char *
581OIDC_build_authz_code (const struct GNUNET_CRYPTO_PrivateKey *issuer,
582 const struct GNUNET_RECLAIM_Ticket *ticket,
583 const struct GNUNET_RECLAIM_AttributeList *attrs,
584 const struct
585 GNUNET_RECLAIM_PresentationList *presentations,
586 const char *nonce_str,
587 const char *code_challenge)
588{
589 struct OIDC_Parameters params;
590 char *code_payload;
591 char *payload;
592 char *tmp;
593 char *code_str;
594 char *buf_ptr = NULL;
595 size_t payload_len;
596 size_t code_payload_len;
597 size_t attr_list_len = 0;
598 size_t pres_list_len = 0;
599 size_t code_challenge_len = 0;
600 uint32_t nonce_len = 0;
601 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
602
603 /** PLAINTEXT **/
604 // Assign ticket
605 memset (&params, 0, sizeof(params));
606 params.ticket = *ticket;
607 // Assign nonce
608 payload_len = sizeof(struct OIDC_Parameters);
609 if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0))
610 {
611 nonce_len = strlen (nonce_str);
612 payload_len += nonce_len;
613 }
614 params.nonce_len = htonl (nonce_len);
615 // Assign code challenge
616 if (NULL != code_challenge)
617 code_challenge_len = strlen (code_challenge);
618 payload_len += code_challenge_len;
619 params.code_challenge_len = htonl (code_challenge_len);
620 // Assign attributes
621 if (NULL != attrs)
622 {
623 // Get length
624 attr_list_len = GNUNET_RECLAIM_attribute_list_serialize_get_size (attrs);
625 params.attr_list_len = htonl (attr_list_len);
626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627 "Length of serialized attributes: %lu\n",
628 attr_list_len);
629 // Get serialized attributes
630 payload_len += attr_list_len;
631 }
632 if (NULL != presentations)
633 {
634 // Get length
635 // FIXME only add presentations relevant for attribute list!!!
636 // This is important because of the distinction between id_token and
637 // userinfo in OIDC
638 pres_list_len =
639 GNUNET_RECLAIM_presentation_list_serialize_get_size (presentations);
640 params.pres_list_len = htonl (pres_list_len);
641 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642 "Length of serialized presentations: %lu\n",
643 pres_list_len);
644 // Get serialized attributes
645 payload_len += pres_list_len;
646 }
647
648 // Get plaintext length
649 payload = GNUNET_malloc (payload_len);
650 memcpy (payload, &params, sizeof(params));
651 tmp = payload + sizeof(params);
652 if (0 < code_challenge_len)
653 {
654 memcpy (tmp, code_challenge, code_challenge_len);
655 tmp += code_challenge_len;
656 }
657 if (0 < nonce_len)
658 {
659 memcpy (tmp, nonce_str, nonce_len);
660 tmp += nonce_len;
661 }
662 if (0 < attr_list_len)
663 GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp);
664 tmp += attr_list_len;
665 if (0 < pres_list_len)
666 GNUNET_RECLAIM_presentation_list_serialize (presentations, tmp);
667 tmp += pres_list_len;
668
669 /** END **/
670
671 // Get length
672 code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
673 + payload_len + sizeof(struct
674 GNUNET_CRYPTO_Signature);
675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676 "Length of data to encode: %lu\n",
677 code_payload_len);
678
679 // Initialize code payload
680 code_payload = GNUNET_malloc (code_payload_len);
681 GNUNET_assert (NULL != code_payload);
682 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
683 purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
684 + payload_len);
685 purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
686 // Store pubkey
687 buf_ptr = (char *) &purpose[1];
688 memcpy (buf_ptr, payload, payload_len);
689 GNUNET_free (payload);
690 buf_ptr += payload_len;
691 // Sign and store signature
692 if (GNUNET_SYSERR ==
693 GNUNET_CRYPTO_sign_ (issuer,
694 purpose,
695 (struct GNUNET_CRYPTO_Signature *)
696 buf_ptr))
697 {
698 GNUNET_break (0);
699 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to sign code\n");
700 GNUNET_free (code_payload);
701 return NULL;
702 }
703 GNUNET_STRINGS_base64url_encode (code_payload, code_payload_len, &code_str);
704 GNUNET_free (code_payload);
705 return code_str;
706}
707
708
709enum GNUNET_GenericReturnValue
710check_code_challenge (const char *code_challenge,
711 uint32_t code_challenge_len,
712 const char *code_verifier)
713{
714 char *code_verifier_hash;
715 char *expected_code_challenge;
716
717 if (0 == code_challenge_len) /* Only check if this code requires a CV */
718 return GNUNET_OK;
719 if (NULL == code_verifier)
720 {
721 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
722 "Expected code verifier!\n");
723 return GNUNET_SYSERR;
724 }
725 code_verifier_hash = GNUNET_malloc (256 / 8);
726 // hash code verifier
727 gcry_md_hash_buffer (GCRY_MD_SHA256,
728 code_verifier_hash,
729 code_verifier,
730 strlen (code_verifier));
731 // encode code verifier
732 GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8,
733 &expected_code_challenge);
734 GNUNET_free (code_verifier_hash);
735 if (0 !=
736 strncmp (expected_code_challenge, code_challenge, code_challenge_len))
737 {
738 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
739 "Invalid code verifier! Expected: %s, Got: %.*s\n",
740 expected_code_challenge,
741 code_challenge_len,
742 code_challenge);
743 GNUNET_free (expected_code_challenge);
744 return GNUNET_SYSERR;
745 }
746 GNUNET_free (expected_code_challenge);
747 return GNUNET_OK;
748}
749
750
751/**
752 * Parse reclaim ticket and nonce from
753 * authorization code.
754 * This also verifies the signature in the code.
755 *
756 * @param audience the expected audience of the code
757 * @param code the string representation of the code
758 * @param code_verfier PKCE code verifier. Optional, must be provided
759 * if used in request.
760 * @param ticket where to store the ticket
761 * @param attrs the attributes in the code
762 * @param presentations credential presentation list
763 * @param nonce_str where to store the nonce (if contained)
764 * @return GNUNET_OK if successful, else GNUNET_SYSERR
765 */
766int
767OIDC_parse_authz_code (const struct GNUNET_CRYPTO_PublicKey *audience,
768 const char *code,
769 const char *code_verifier,
770 struct GNUNET_RECLAIM_Ticket *ticket,
771 struct GNUNET_RECLAIM_AttributeList **attrs,
772 struct GNUNET_RECLAIM_PresentationList **presentations,
773 char **nonce_str,
774 enum OIDC_VerificationOptions opts)
775{
776 char *code_payload;
777 char *ptr;
778 char *plaintext;
779 char *attrs_ser;
780 char *presentations_ser;
781 char *code_challenge;
782 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
783 struct GNUNET_CRYPTO_Signature *signature;
784 uint32_t code_challenge_len;
785 uint32_t attrs_ser_len;
786 uint32_t pres_ser_len;
787 size_t plaintext_len;
788 size_t code_payload_len;
789 uint32_t nonce_len = 0;
790 struct OIDC_Parameters *params;
791
792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code);
793 code_payload = NULL;
794 code_payload_len =
795 GNUNET_STRINGS_base64url_decode (code, strlen (code),
796 (void **) &code_payload);
797 if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
798 + sizeof(struct OIDC_Parameters)
799 + sizeof(struct GNUNET_CRYPTO_Signature))
800 {
801 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Authorization code malformed\n");
802 GNUNET_free (code_payload);
803 return GNUNET_SYSERR;
804 }
805
806 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
807 plaintext_len = code_payload_len;
808 plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose);
809 ptr = (char *) &purpose[1];
810 plaintext_len -= sizeof(struct GNUNET_CRYPTO_Signature);
811 plaintext = ptr;
812 ptr += plaintext_len;
813 signature = (struct GNUNET_CRYPTO_Signature *) ptr;
814 params = (struct OIDC_Parameters *) plaintext;
815
816 // cmp code_challenge code_verifier
817 code_challenge_len = ntohl (params->code_challenge_len);
818 code_challenge = ((char *) &params[1]);
819 if (! (opts & OIDC_VERIFICATION_NO_CODE_VERIFIER))
820 {
821 if (GNUNET_OK != check_code_challenge (code_challenge,
822 code_challenge_len,
823 code_verifier))
824 {
825 GNUNET_free (code_payload);
826 return GNUNET_SYSERR;
827 }
828 }
829 nonce_len = ntohl (params->nonce_len);
830 if (0 != nonce_len)
831 {
832 *nonce_str = GNUNET_strndup (code_challenge + code_challenge_len,
833 nonce_len);
834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %s\n", *nonce_str);
835 }
836
837 // Ticket
838 memcpy (ticket, &params->ticket, sizeof(params->ticket));
839 // Signature
840 // GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub);
841 if (0 != GNUNET_memcmp (audience, &ticket->audience))
842 {
843 GNUNET_free (code_payload);
844 if (NULL != *nonce_str)
845 GNUNET_free (*nonce_str);
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "Audience in ticket does not match client!\n");
848 return GNUNET_SYSERR;
849 }
850 if (GNUNET_OK !=
851 GNUNET_CRYPTO_signature_verify_ (
852 GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN,
853 purpose,
854 signature,
855 &(ticket->identity)))
856 {
857 GNUNET_free (code_payload);
858 if (NULL != *nonce_str)
859 GNUNET_free (*nonce_str);
860 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n");
861 return GNUNET_SYSERR;
862 }
863 // Attributes
864 attrs_ser = ((char *) &params[1]) + code_challenge_len + nonce_len;
865 attrs_ser_len = ntohl (params->attr_list_len);
866 *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len);
867 presentations_ser = ((char*) attrs_ser) + attrs_ser_len;
868 pres_ser_len = ntohl (params->pres_list_len);
869 *presentations =
870 GNUNET_RECLAIM_presentation_list_deserialize (presentations_ser,
871 pres_ser_len);
872
873 GNUNET_free (code_payload);
874 return GNUNET_OK;
875}
876
877
878/**
879 * Build a token response for a token request
880 * TODO: Maybe we should add the scope here?
881 *
882 * @param access_token the access token to include
883 * @param id_token the id_token to include
884 * @param expiration_time the expiration time of the token(s)
885 * @param token_response where to store the response
886 */
887void
888OIDC_build_token_response (const char *access_token,
889 const char *id_token,
890 const struct GNUNET_TIME_Relative *expiration_time,
891 char **token_response)
892{
893 json_t *root_json;
894
895 root_json = json_object ();
896
897 GNUNET_assert (NULL != access_token);
898 GNUNET_assert (NULL != id_token);
899 GNUNET_assert (NULL != expiration_time);
900 json_object_set_new (root_json, "access_token", json_string (access_token));
901 json_object_set_new (root_json, "token_type", json_string ("Bearer"));
902 json_object_set_new (root_json,
903 "expires_in",
904 json_integer (expiration_time->rel_value_us
905 / (1000 * 1000)));
906 json_object_set_new (root_json, "id_token", json_string (id_token));
907 *token_response = json_dumps (root_json, JSON_INDENT (0) | JSON_COMPACT);
908 json_decref (root_json);
909}
910
911
912/**
913 * Generate a new access token
914 */
915char *
916OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket)
917{
918 char *access_token;
919
920 GNUNET_STRINGS_base64_encode (ticket,
921 sizeof(*ticket),
922 &access_token);
923 return access_token;
924}
925
926
927/**
928 * Parse an access token
929 */
930int
931OIDC_access_token_parse (const char *token,
932 struct GNUNET_RECLAIM_Ticket **ticket)
933{
934 size_t sret;
935 char *decoded;
936 sret = GNUNET_STRINGS_base64_decode (token,
937 strlen (token),
938 (void**) &decoded);
939 if (sizeof (struct GNUNET_RECLAIM_Ticket) != sret)
940 {
941 GNUNET_free (decoded);
942 return GNUNET_SYSERR;
943 }
944 *ticket = (struct GNUNET_RECLAIM_Ticket *) decoded;
945 return GNUNET_OK;
946}
947
948
949/**
950 * Checks if a claim is implicitly requested through standard
951 * scope(s) or explicitly through non-standard scope.
952 *
953 * @param scopes the scopes which have been requested
954 * @param attr the attribute name to check
955 * @return GNUNET_YES if attribute is implcitly requested
956 */
957enum GNUNET_GenericReturnValue
958OIDC_check_scopes_for_claim_request (const char*scopes,
959 const char*attr)
960{
961 char *scope_variables;
962 char *scope_variable;
963 char delimiter[] = " ";
964 int i;
965
966 scope_variables = GNUNET_strdup (scopes);
967 scope_variable = strtok (scope_variables, delimiter);
968 while (NULL != scope_variable)
969 {
970 if (0 == strcmp ("profile", scope_variable))
971 {
972 for (i = 0; i < 14; i++)
973 {
974 if (0 == strcmp (attr, OIDC_profile_claims[i]))
975 {
976 GNUNET_free (scope_variables);
977 return GNUNET_YES;
978 }
979 }
980 }
981 else if (0 == strcmp ("address", scope_variable))
982 {
983 for (i = 0; i < 5; i++)
984 {
985 if (0 == strcmp (attr, OIDC_address_claims[i]))
986 {
987 GNUNET_free (scope_variables);
988 return GNUNET_YES;
989 }
990 }
991 }
992 else if (0 == strcmp ("email", scope_variable))
993 {
994 for (i = 0; i < 2; i++)
995 {
996 if (0 == strcmp (attr, OIDC_email_claims[i]))
997 {
998 GNUNET_free (scope_variables);
999 return GNUNET_YES;
1000 }
1001 }
1002 }
1003 else if (0 == strcmp ("phone", scope_variable))
1004 {
1005 for (i = 0; i < 2; i++)
1006 {
1007 if (0 == strcmp (attr, OIDC_phone_claims[i]))
1008 {
1009 GNUNET_free (scope_variables);
1010 return GNUNET_YES;
1011 }
1012 }
1013
1014 }
1015 else if (0 == strcmp (attr, scope_variable))
1016 {
1017 /** attribute matches requested scope **/
1018 GNUNET_free (scope_variables);
1019 return GNUNET_YES;
1020 }
1021 scope_variable = strtok (NULL, delimiter);
1022 }
1023 GNUNET_free (scope_variables);
1024 return GNUNET_NO;
1025
1026}
diff --git a/src/rest-plugin/reclaim/oidc_helper.h b/src/rest-plugin/reclaim/oidc_helper.h
deleted file mode 100644
index de788fbdb..000000000
--- a/src/rest-plugin/reclaim/oidc_helper.h
+++ /dev/null
@@ -1,196 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file reclaim/oidc_helper.h
23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach
25 */
26
27#ifndef JWT_H
28#define JWT_H
29
30#define JWT_ALG "alg"
31#define JWT_TYP "typ"
32#define JWT_TYP_VALUE "jwt"
33
34#define JWT_ALG_VALUE_HMAC "HS512"
35#define JWT_ALG_VALUE_RSA "RS256"
36
37#define SERVER_ADDRESS "http://localhost:7776"
38
39enum OIDC_VerificationOptions
40{
41 /**
42 * Strict verification
43 */
44 OIDC_VERIFICATION_DEFAULT = 0,
45
46 /**
47 * Do not check code verifier even if expected
48 */
49 OIDC_VERIFICATION_NO_CODE_VERIFIER = 1
50};
51
52/**
53 * Create a JWT using RSA256 algorithm from attributes
54 *
55 * @param aud_key the public of the audience
56 * @param sub_key the public key of the subject
57 * @param attrs the attribute list
58 * @param presentations credential presentation list (may be empty)
59 * @param expiration_time the validity of the token
60 * @param secret_rsa_key the key used to sign the JWT
61 * @return a new base64-encoded JWT string.
62 */
63char *
64OIDC_generate_id_token_rsa (const struct GNUNET_CRYPTO_PublicKey *aud_key,
65 const struct GNUNET_CRYPTO_PublicKey *sub_key,
66 const struct GNUNET_RECLAIM_AttributeList *attrs,
67 const struct
68 GNUNET_RECLAIM_PresentationList *presentations,
69 const struct GNUNET_TIME_Relative *expiration_time,
70 const char *nonce,
71 const json_t *secret_rsa_key);
72
73/**
74 * Create a JWT using HMAC (HS256) from attributes
75 *
76 * @param aud_key the public of the audience
77 * @param sub_key the public key of the subject
78 * @param attrs the attribute list
79 * @param presentations credential presentation list (may be empty)
80 * @param expiration_time the validity of the token
81 * @param secret_key the key used to sign the JWT
82 * @return a new base64-encoded JWT string.
83 */
84char*
85OIDC_generate_id_token_hmac (const struct GNUNET_CRYPTO_PublicKey *aud_key,
86 const struct GNUNET_CRYPTO_PublicKey *sub_key,
87 const struct GNUNET_RECLAIM_AttributeList *attrs,
88 const struct
89 GNUNET_RECLAIM_PresentationList *presentations,
90 const struct GNUNET_TIME_Relative *expiration_time,
91 const char *nonce,
92 const char *secret_key);
93
94/**
95 * Builds an OIDC authorization code including
96 * a reclaim ticket and nonce
97 *
98 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
99 * @param ticket the ticket to include in the code
100 * @param attrs list of attributes to share
101 * @param presentations credential presentation list
102 * @param nonce the nonce to include in the code
103 * @param code_challenge PKCE code challenge
104 * @param opts verification options
105 * @return a new authorization code (caller must free)
106 */
107char*
108OIDC_build_authz_code (const struct GNUNET_CRYPTO_PrivateKey *issuer,
109 const struct GNUNET_RECLAIM_Ticket *ticket,
110 const struct GNUNET_RECLAIM_AttributeList *attrs,
111 const struct
112 GNUNET_RECLAIM_PresentationList *presentations,
113 const char *nonce,
114 const char *code_challenge);
115
116/**
117 * Parse reclaim ticket and nonce from
118 * authorization code.
119 * This also verifies the signature in the code.
120 *
121 * @param ecdsa_priv the audience of the ticket
122 * @param code the string representation of the code
123 * @param code_verfier PKCE code verifier
124 * @param ticket where to store the ticket
125 * @param attrs the attributes found in the code
126 * @param presentations credential presentation list
127 * @param nonce where to store the nonce
128 * @return GNUNET_OK if successful, else GNUNET_SYSERR
129 */
130int
131OIDC_parse_authz_code (const struct GNUNET_CRYPTO_PublicKey *ecdsa_pub,
132 const char *code,
133 const char *code_verifier,
134 struct GNUNET_RECLAIM_Ticket *ticket,
135 struct GNUNET_RECLAIM_AttributeList **attrs,
136 struct GNUNET_RECLAIM_PresentationList **presentations,
137 char **nonce,
138 enum OIDC_VerificationOptions opts);
139
140/**
141 * Build a token response for a token request
142 * TODO: Maybe we should add the scope here?
143 *
144 * @param access_token the access token to include
145 * @param id_token the id_token to include
146 * @param expiration_time the expiration time of the token(s)
147 * @param token_response where to store the response
148 */
149void
150OIDC_build_token_response (const char *access_token,
151 const char *id_token,
152 const struct GNUNET_TIME_Relative *expiration_time,
153 char **token_response);
154
155/**
156 * Generate a new access token
157 */
158char*
159OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket);
160
161/**
162 * Parse an access token
163 */
164int
165OIDC_access_token_parse (const char *token,
166 struct GNUNET_RECLAIM_Ticket **ticket);
167
168
169/**
170 * Checks if a claim is implicitly requested through standard
171 * scope(s)
172 *
173 * @param scopes the scopes which have been requested
174 * @param attr the attribute name to check
175 * @return GNUNET_YES if attribute is implcitly requested
176 */
177enum GNUNET_GenericReturnValue
178OIDC_check_scopes_for_claim_request (const char *scopes,
179 const char *attr);
180
181
182/**
183 * Generate userinfo JSON as string
184 *
185 * @param sub_key the subject (user)
186 * @param attrs user attribute list
187 * @param presentations credential presentation list
188 * @return Userinfo JSON
189 */
190char *
191OIDC_generate_userinfo (const struct GNUNET_CRYPTO_PublicKey *sub_key,
192 const struct GNUNET_RECLAIM_AttributeList *attrs,
193 const struct
194 GNUNET_RECLAIM_PresentationList *presentations);
195
196#endif
diff --git a/src/rest-plugin/reclaim/plugin_rest_openid_connect.c b/src/rest-plugin/reclaim/plugin_rest_openid_connect.c
deleted file mode 100644
index d0aee043e..000000000
--- a/src/rest-plugin/reclaim/plugin_rest_openid_connect.c
+++ /dev/null
@@ -1,3162 +0,0 @@
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 * @author Tristan Schwieren
24 * @file identity/plugin_rest_openid_connect.c
25 * @brief GNUnet Namestore REST plugin
26 *
27 */
28#include "platform.h"
29#include <inttypes.h>
30#include <jansson.h>
31#include <jose/jose.h>
32
33#include "gnunet_util_lib.h"
34#include "gnunet_gns_service.h"
35#include "gnunet_gnsrecord_lib.h"
36#include "gnunet_identity_service.h"
37#include "gnunet_namestore_service.h"
38#include "gnunet_reclaim_lib.h"
39#include "gnunet_reclaim_service.h"
40#include "gnunet_rest_lib.h"
41#include "gnunet_rest_plugin.h"
42#include "gnunet_signatures.h"
43#include "microhttpd.h"
44#include "oidc_helper.h"
45
46/**
47 * REST root namespace
48 */
49#define GNUNET_REST_API_NS_OIDC "/openid"
50
51/**
52 * OIDC config
53 */
54#define GNUNET_REST_API_NS_OIDC_CONFIG "/.well-known/openid-configuration"
55
56/**
57 * Authorize endpoint
58 */
59#define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
60
61/**
62 * Token endpoint
63 */
64#define GNUNET_REST_API_NS_TOKEN "/openid/token"
65
66/**
67 * JSON Web Keys endpoint
68 */
69#define GNUNET_REST_API_JWKS "/jwks.json"
70
71/**
72 * UserInfo endpoint
73 */
74#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
75
76/**
77 * Login namespace
78 */
79#define GNUNET_REST_API_NS_LOGIN "/openid/login"
80
81/**
82 * State while collecting all egos
83 */
84#define ID_REST_STATE_INIT 0
85
86/**
87 * Done collecting egos
88 */
89#define ID_REST_STATE_POST_INIT 1
90
91/**
92 * OIDC grant_type key
93 */
94#define OIDC_GRANT_TYPE_KEY "grant_type"
95
96/**
97 * OIDC grant_type key
98 */
99#define OIDC_GRANT_TYPE_VALUE "authorization_code"
100
101/**
102 * OIDC code key
103 */
104#define OIDC_CODE_KEY "code"
105
106/**
107 * OIDC response_type key
108 */
109#define OIDC_RESPONSE_TYPE_KEY "response_type"
110
111/**
112 * OIDC client_id key
113 */
114#define OIDC_CLIENT_ID_KEY "client_id"
115
116/**
117 * OIDC scope key
118 */
119#define OIDC_SCOPE_KEY "scope"
120
121/**
122 * OIDC redirect_uri key
123 */
124#define OIDC_REDIRECT_URI_KEY "redirect_uri"
125
126/**
127 * OIDC state key
128 */
129#define OIDC_STATE_KEY "state"
130
131/**
132 * OIDC nonce key
133 */
134#define OIDC_NONCE_KEY "nonce"
135
136/**
137 * OIDC claims key
138 */
139#define OIDC_CLAIMS_KEY "claims"
140
141/**
142 * OIDC PKCE code challenge
143 */
144#define OIDC_CODE_CHALLENGE_KEY "code_challenge"
145
146/**
147 * OIDC PKCE code verifier
148 */
149#define OIDC_CODE_VERIFIER_KEY "code_verifier"
150
151/**
152 * OIDC cookie expiration (in seconds)
153 */
154#define OIDC_COOKIE_EXPIRATION 3
155
156/**
157 * OIDC cookie header key
158 */
159#define OIDC_COOKIE_HEADER_KEY "cookie"
160
161/**
162 * OIDC cookie header information key
163 */
164#define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
165
166/**
167 * OIDC cookie header information key
168 */
169#define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
170
171/**
172 * OIDC cookie header if user cancelled
173 */
174#define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
175
176/**
177 * OIDC expected response_type while authorizing
178 */
179#define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
180
181/**
182 * OIDC expected scope part while authorizing
183 */
184#define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
185
186/**
187 * OIDC error key for invalid client
188 */
189#define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
190
191/**
192 * OIDC error key for invalid scopes
193 */
194#define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
195
196/**
197 * OIDC error key for invalid requests
198 */
199#define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
200
201/**
202 * OIDC error key for invalid tokens
203 */
204#define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
205
206/**
207 * OIDC error key for invalid cookies
208 */
209#define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
210
211/**
212 * OIDC error key for generic server errors
213 */
214#define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
215
216/**
217 * OIDC error key for unsupported grants
218 */
219#define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
220
221/**
222 * OIDC error key for unsupported response types
223 */
224#define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
225
226/**
227 * OIDC error key for unauthorized clients
228 */
229#define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
230
231/**
232 * OIDC error key for denied access
233 */
234#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
235
236/**
237 * OIDC key store file name
238 */
239#define OIDC_JWK_RSA_FILENAME "jwk_rsa.json"
240
241/**
242 * How long to wait for a consume in userinfo endpoint
243 */
244#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \
245 GNUNET_TIME_UNIT_SECONDS,2)
246
247/**
248 * OIDC ignored parameter array
249 */
250static char *OIDC_ignored_parameter_array[] = { "display",
251 "prompt",
252 "ui_locales",
253 "response_mode",
254 "id_token_hint",
255 "login_hint",
256 "acr_values" };
257
258/**
259 * OIDC hashmap for cached access tokens and codes
260 */
261struct GNUNET_CONTAINER_MultiHashMap *oidc_code_cache;
262
263/**
264 * OIDC hashmap that keeps track of issued cookies
265 */
266struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
267
268/**
269 * The configuration handle
270 */
271const struct GNUNET_CONFIGURATION_Handle *cfg;
272
273/**
274 * HTTP methods allows for this plugin
275 */
276static char *allow_methods;
277
278/**
279 * Ego list
280 */
281static struct EgoEntry *ego_head;
282
283/**
284 * Ego list
285 */
286static struct EgoEntry *ego_tail;
287
288/**
289 * The processing state
290 */
291static int state;
292
293/**
294 * Handle to Identity service.
295 */
296static struct GNUNET_IDENTITY_Handle *identity_handle;
297
298/**
299 * GNS handle
300 */
301static struct GNUNET_GNS_Handle *gns_handle;
302
303/**
304 * Identity Provider
305 */
306static struct GNUNET_RECLAIM_Handle *idp;
307
308/**
309 * Timeout for consume call on userinfo
310 */
311static struct GNUNET_TIME_Relative consume_timeout;
312
313/**
314 * @brief struct returned by the initialization function of the plugin
315 */
316struct Plugin
317{
318 const struct GNUNET_CONFIGURATION_Handle *cfg;
319};
320
321/**
322 * @brief The RSA key used by the oidc enpoint
323 */
324json_t *oidc_jwk;
325
326/**
327 * OIDC needed variables
328 */
329struct OIDC_Variables
330{
331 /**
332 * The RP client public key
333 */
334 struct GNUNET_CRYPTO_PublicKey client_pkey;
335
336 /**
337 * The OIDC client id of the RP
338 */
339 char *client_id;
340
341 /**
342 * The OIDC redirect uri
343 */
344 char *redirect_uri;
345
346 /**
347 * The list of oidc scopes
348 */
349 char *scope;
350
351 /**
352 * The OIDC state
353 */
354 char *state;
355
356 /**
357 * The OIDC nonce
358 */
359 char *nonce;
360
361 /**
362 * The OIDC claims
363 */
364 char *claims;
365
366 /**
367 * The OIDC response type
368 */
369 char *response_type;
370
371 /**
372 * The identity chosen by the user to login
373 */
374 char *login_identity;
375
376 /**
377 * User cancelled authorization/login
378 */
379 int user_cancelled;
380
381 /**
382 * The PKCE code_challenge
383 */
384 char *code_challenge;
385
386 /**
387 * The PKCE code_verifier
388 */
389 char *code_verifier;
390
391};
392
393/**
394 * The ego list
395 */
396struct EgoEntry
397{
398 /**
399 * DLL
400 */
401 struct EgoEntry *next;
402
403 /**
404 * DLL
405 */
406 struct EgoEntry *prev;
407
408 /**
409 * Ego Identifier
410 */
411 char *identifier;
412
413 /**
414 * Public key string
415 */
416 char *keystring;
417
418 /**
419 * The Ego
420 */
421 struct GNUNET_IDENTITY_Ego *ego;
422};
423
424
425struct RequestHandle
426{
427 /**
428 * DLL
429 */
430 struct RequestHandle *next;
431
432 /**
433 * DLL
434 */
435 struct RequestHandle *prev;
436
437 /**
438 * Selected ego
439 */
440 struct EgoEntry *ego_entry;
441
442 /**
443 * Pointer to ego private key
444 */
445 struct GNUNET_CRYPTO_PrivateKey priv_key;
446
447 /**
448 * OIDC variables
449 */
450 struct OIDC_Variables *oidc;
451
452 /**
453 * GNS lookup op
454 */
455 struct GNUNET_GNS_LookupRequest *gns_op;
456
457 /**
458 * Rest connection
459 */
460 struct GNUNET_REST_RequestHandle *rest_handle;
461
462 /**
463 * Attribute claim list for id_token
464 */
465 struct GNUNET_RECLAIM_AttributeList *attr_idtoken_list;
466
467 /**
468 * Attribute claim list for userinfo
469 */
470 struct GNUNET_RECLAIM_AttributeList *attr_userinfo_list;
471
472 /**
473 * Credentials
474 */
475 struct GNUNET_RECLAIM_CredentialList *credentials;
476
477 /**
478 * Presentations
479 */
480 struct GNUNET_RECLAIM_PresentationList *presentations;
481
482 /**
483 * IDENTITY Operation
484 */
485 struct GNUNET_IDENTITY_Operation *op;
486
487
488 /**
489 * Idp Operation
490 */
491 struct GNUNET_RECLAIM_Operation *idp_op;
492
493 /**
494 * Timeout task for consume
495 */
496 struct GNUNET_SCHEDULER_Task *consume_timeout_op;
497
498 /**
499 * Attribute iterator
500 */
501 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
502
503 /**
504 * Credential iterator
505 */
506 struct GNUNET_RECLAIM_CredentialIterator *cred_it;
507
508
509 /**
510 * Ticket iterator
511 */
512 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
513
514 /**
515 * A ticket
516 */
517 struct GNUNET_RECLAIM_Ticket ticket;
518
519 /**
520 * Desired timeout for the lookup (default is no timeout).
521 */
522 struct GNUNET_TIME_Relative timeout;
523
524 /**
525 * ID of a task associated with the resolution process.
526 */
527 struct GNUNET_SCHEDULER_Task *timeout_task;
528
529 /**
530 * The plugin result processor
531 */
532 GNUNET_REST_ResultProcessor proc;
533
534 /**
535 * The closure of the result processor
536 */
537 void *proc_cls;
538
539 /**
540 * The url
541 */
542 char *url;
543
544 /**
545 * The passed access token
546 */
547 char *access_token;
548
549 /**
550 * The tld for redirect
551 */
552 char *tld;
553
554 /**
555 * The redirect prefix
556 */
557 char *redirect_prefix;
558
559 /**
560 * The redirect suffix
561 */
562 char *redirect_suffix;
563
564 /**
565 * Error response message
566 */
567 char *emsg;
568
569 /**
570 * Error response description
571 */
572 char *edesc;
573
574 /**
575 * Response code
576 */
577 int response_code;
578
579 /**
580 * Public client
581 */
582 int public_client;
583};
584
585/**
586 * DLL
587 */
588static struct RequestHandle *requests_head;
589
590/**
591 * DLL
592 */
593static struct RequestHandle *requests_tail;
594
595
596/**
597 * Cleanup lookup handle
598 * @param handle Handle to clean up
599 */
600static void
601cleanup_handle (struct RequestHandle *handle)
602{
603
604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
605 if (NULL != handle->timeout_task)
606 GNUNET_SCHEDULER_cancel (handle->timeout_task);
607 if (NULL != handle->attr_it)
608 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
609 if (NULL != handle->cred_it)
610 GNUNET_RECLAIM_get_credentials_stop (handle->cred_it);
611 if (NULL != handle->ticket_it)
612 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
613 if (NULL != handle->idp_op)
614 GNUNET_RECLAIM_cancel (handle->idp_op);
615 if (NULL != handle->consume_timeout_op)
616 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
617 GNUNET_free (handle->url);
618 GNUNET_free (handle->tld);
619 GNUNET_free (handle->redirect_prefix);
620 GNUNET_free (handle->redirect_suffix);
621 GNUNET_free (handle->emsg);
622 GNUNET_free (handle->edesc);
623 if (NULL != handle->gns_op)
624 GNUNET_GNS_lookup_cancel (handle->gns_op);
625 if (NULL != handle->oidc)
626 {
627 GNUNET_free (handle->oidc->client_id);
628 GNUNET_free (handle->oidc->login_identity);
629 GNUNET_free (handle->oidc->nonce);
630 GNUNET_free (handle->oidc->redirect_uri);
631 GNUNET_free (handle->oidc->response_type);
632 GNUNET_free (handle->oidc->scope);
633 GNUNET_free (handle->oidc->state);
634 if (NULL != handle->oidc->claims)
635 GNUNET_free (handle->oidc->claims);
636 if (NULL != handle->oidc->code_challenge)
637 GNUNET_free (handle->oidc->code_challenge);
638 GNUNET_free (handle->oidc);
639 }
640 if (NULL!=handle->attr_idtoken_list)
641 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_idtoken_list);
642 if (NULL!=handle->attr_userinfo_list)
643 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_userinfo_list);
644 if (NULL!=handle->credentials)
645 GNUNET_RECLAIM_credential_list_destroy (handle->credentials);
646 if (NULL!=handle->presentations)
647 GNUNET_RECLAIM_presentation_list_destroy (handle->presentations);
648 GNUNET_CONTAINER_DLL_remove (requests_head,
649 requests_tail,
650 handle);
651 if (NULL != handle->access_token)
652 GNUNET_free (handle->access_token);
653 GNUNET_free (handle);
654}
655
656
657/**
658 * Task run on error, sends error message. Cleans up everything.
659 *
660 * @param cls the `struct RequestHandle`
661 */
662static void
663do_error (void *cls)
664{
665 struct RequestHandle *handle = cls;
666 struct MHD_Response *resp;
667 char *json_error;
668
669 GNUNET_asprintf (&json_error,
670 "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
671 handle->emsg,
672 (NULL != handle->edesc) ? handle->edesc : "",
673 (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
674 (NULL != handle->oidc->state) ? handle->oidc->state : "",
675 (NULL != handle->oidc->state) ? "\"" : "");
676 if (0 == handle->response_code)
677 handle->response_code = MHD_HTTP_BAD_REQUEST;
678 resp = GNUNET_REST_create_response (json_error);
679 if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
680 GNUNET_assert (MHD_NO !=
681 MHD_add_response_header (resp,
682 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
683 "Basic"));
684 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
685 MHD_HTTP_HEADER_CONTENT_TYPE,
686 "application/json"));
687 handle->proc (handle->proc_cls, resp, handle->response_code);
688 cleanup_handle (handle);
689 GNUNET_free (json_error);
690}
691
692
693/**
694 * Task run on error in userinfo endpoint, sends error header. Cleans up
695 * everything
696 *
697 * @param cls the `struct RequestHandle`
698 */
699static void
700do_userinfo_error (void *cls)
701{
702 struct RequestHandle *handle = cls;
703 struct MHD_Response *resp;
704 char *error;
705
706 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707 "Error: %s\n", handle->edesc);
708 GNUNET_asprintf (&error,
709 "error=\"%s\", error_description=\"%s\"",
710 handle->emsg,
711 (NULL != handle->edesc) ? handle->edesc : "");
712 resp = GNUNET_REST_create_response ("");
713 GNUNET_assert (MHD_NO !=
714 MHD_add_response_header (resp,
715 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
716 "Bearer"));
717 handle->proc (handle->proc_cls, resp, handle->response_code);
718 cleanup_handle (handle);
719 GNUNET_free (error);
720}
721
722
723/**
724 * Task run on error, sends error message and redirects. Cleans up everything.
725 *
726 * @param cls the `struct RequestHandle`
727 */
728static void
729do_redirect_error (void *cls)
730{
731 struct RequestHandle *handle = cls;
732 struct MHD_Response *resp;
733 char *redirect;
734
735 GNUNET_asprintf (&redirect,
736 "%s?error=%s&error_description=%s%s%s",
737 handle->oidc->redirect_uri,
738 handle->emsg,
739 handle->edesc,
740 (NULL != handle->oidc->state) ? "&state=" : "",
741 (NULL != handle->oidc->state) ? handle->oidc->state : "");
742 resp = GNUNET_REST_create_response ("");
743 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
744 "Location", redirect));
745 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
746 cleanup_handle (handle);
747 GNUNET_free (redirect);
748}
749
750
751/**
752 * Task run on timeout, sends error message. Cleans up everything.
753 *
754 * @param cls the `struct RequestHandle`
755 */
756static void
757do_timeout (void *cls)
758{
759 struct RequestHandle *handle = cls;
760
761 handle->timeout_task = NULL;
762 do_error (handle);
763}
764
765
766/**
767 * Respond to OPTIONS request
768 *
769 * @param con_handle the connection handle
770 * @param url the url
771 * @param cls the RequestHandle
772 */
773static void
774options_cont (struct GNUNET_REST_RequestHandle *con_handle,
775 const char *url,
776 void *cls)
777{
778 struct MHD_Response *resp;
779 struct RequestHandle *handle = cls;
780
781 // For now, independent of path return all options
782 resp = GNUNET_REST_create_response (NULL);
783 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
784 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
785 cleanup_handle (handle);
786 return;
787}
788
789
790/**
791 * Interprets cookie header and pass its identity keystring to handle
792 */
793static void
794cookie_identity_interpretation (struct RequestHandle *handle)
795{
796 struct GNUNET_HashCode cache_key;
797 char *cookies;
798 struct GNUNET_TIME_Absolute current_time, *relog_time;
799 char delimiter[] = "; ";
800 char *tmp_cookies;
801 char *token;
802 char *value;
803
804 // gets identity of login try with cookie
805 GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
806 strlen (OIDC_COOKIE_HEADER_KEY),
807 &cache_key);
808 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
809 ->header_param_map,
810 &cache_key))
811 {
812 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
813 return;
814 }
815 // splits cookies and find 'Identity' cookie
816 tmp_cookies =
817 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
818 &cache_key);
819 cookies = GNUNET_strdup (tmp_cookies);
820 token = strtok (cookies, delimiter);
821 handle->oidc->user_cancelled = GNUNET_NO;
822 handle->oidc->login_identity = NULL;
823 if (NULL == token)
824 {
825 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826 "Unable to parse cookie: %s\n",
827 cookies);
828 GNUNET_free (cookies);
829 return;
830 }
831
832 while (NULL != token)
833 {
834 if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
835 {
836 handle->oidc->user_cancelled = GNUNET_YES;
837 GNUNET_free (cookies);
838 return;
839 }
840 if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
841 break;
842 token = strtok (NULL, delimiter);
843 }
844 if (NULL == token)
845 {
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "No cookie value to process: %s\n",
848 cookies);
849 GNUNET_free (cookies);
850 return;
851 }
852 GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
853 if (GNUNET_NO ==
854 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
855 {
856 GNUNET_log (
857 GNUNET_ERROR_TYPE_WARNING,
858 "Found cookie `%s', but no corresponding expiration entry present...\n",
859 token);
860 GNUNET_free (cookies);
861 return;
862 }
863 relog_time =
864 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
865 current_time = GNUNET_TIME_absolute_get ();
866 // 30 min after old login -> redirect to login
867 if (current_time.abs_value_us > relog_time->abs_value_us)
868 {
869 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
870 "Found cookie `%s', but it is expired.\n",
871 token);
872 GNUNET_free (cookies);
873 return;
874 }
875 value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
876 GNUNET_assert (NULL != value);
877 handle->oidc->login_identity = GNUNET_strdup (value);
878 GNUNET_free (cookies);
879}
880
881
882/**
883 * @brief Read the the JSON Web Key in the given file and return it.
884 * Return NULL and emit warning if JSON can not be decoded or the key is
885 * invalid
886 *
887 * @param filename the file to read the JWK from
888 * @return json_t* the reed JWK
889 */
890json_t *
891read_jwk_from_file (const char *filename)
892{
893 json_t *jwk;
894 json_error_t error;
895
896 jwk = json_load_file (filename, JSON_DECODE_ANY, &error);
897
898 if (! jwk)
899 {
900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
901 ("Could not read OIDC RSA key from config file; %s\n"),
902 error.text);
903 }
904
905 return jwk;
906}
907
908/**
909 * @brief Write the JWK to file. If unsuccessful emit warning
910 *
911 * @param filename the name of the file the JWK is writen to
912 * @param jwk the JWK that is going to be written
913 * @return int Return GNUNET_OK if write is sucessfull
914 */
915static int
916write_jwk_to_file (const char *filename,
917 json_t *jwk)
918{
919 if (json_dump_file (jwk, filename, JSON_INDENT (2)))
920 {
921 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
922 ("Could not write OIDC RSA key to file %s\n"),
923 filename);
924 return GNUNET_ERROR_TYPE_WARNING;
925 }
926 else
927 return GNUNET_OK;
928}
929
930/**
931 * @brief Generate a new RSA JSON Web Key
932 *
933 * @return json_t* the generated JWK
934 */
935json_t *
936generate_jwk ()
937{
938 json_t *jwk;
939 jwk = json_pack ("{s:s,s:i}", "kty", "RSA", "bits", 2048);
940 jose_jwk_gen (NULL, jwk);
941 json_incref (jwk);
942 return jwk;
943}
944
945/**
946 * Return the path to the oidc directory path
947 *
948 * @param cls the RequestHandle
949 */
950char *
951get_oidc_dir_path (void *cls)
952{
953 char *oidc_directory;
954 struct RequestHandle *handle = cls;
955
956 // Read OIDC directory from config
957 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
958 "reclaim-rest-plugin",
959 "oidc_dir",
960 &oidc_directory))
961 {
962 // Could not read Config file
963 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
964 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
965 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
966 GNUNET_SCHEDULER_add_now (&do_error, handle);
967 return NULL;
968 }
969
970 return oidc_directory;
971}
972
973/**
974 * Return the path to the RSA JWK key file
975 *
976 * @param cls the RequestHandle
977 */
978char *
979get_oidc_jwk_path (void *cls)
980{
981 char *oidc_directory;
982 char *oidc_jwk_path;
983
984 oidc_directory = get_oidc_dir_path (cls);
985
986 // Create path to file
987 GNUNET_asprintf (&oidc_jwk_path, "%s/%s", oidc_directory,
988 OIDC_JWK_RSA_FILENAME);
989
990 return oidc_jwk_path;
991}
992
993
994/**
995 * Redirects to login page stored in configuration file
996 */
997static void
998login_redirect (void *cls)
999{
1000 char *login_base_url;
1001 char *new_redirect;
1002 char *tmp;
1003 struct MHD_Response *resp;
1004 struct GNUNET_Buffer buf = { 0 };
1005 struct RequestHandle *handle = cls;
1006
1007 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1008 "reclaim-rest-plugin",
1009 "address",
1010 &login_base_url))
1011 {
1012 GNUNET_buffer_write_str (&buf, login_base_url);
1013 GNUNET_buffer_write_fstr (&buf,
1014 "?%s=%s",
1015 OIDC_RESPONSE_TYPE_KEY,
1016 handle->oidc->response_type);
1017 GNUNET_buffer_write_fstr (&buf,
1018 "&%s=%s",
1019 OIDC_CLIENT_ID_KEY,
1020 handle->oidc->client_id);
1021 GNUNET_STRINGS_urlencode (handle->oidc->redirect_uri,
1022 strlen (handle->oidc->redirect_uri),
1023 &tmp);
1024 GNUNET_buffer_write_fstr (&buf,
1025 "&%s=%s",
1026 OIDC_REDIRECT_URI_KEY,
1027 tmp);
1028 GNUNET_free (tmp);
1029 GNUNET_STRINGS_urlencode (handle->oidc->scope,
1030 strlen (handle->oidc->scope),
1031 &tmp);
1032 GNUNET_buffer_write_fstr (&buf,
1033 "&%s=%s",
1034 OIDC_SCOPE_KEY,
1035 tmp);
1036 GNUNET_free (tmp);
1037 if (NULL != handle->oidc->state)
1038 {
1039 GNUNET_STRINGS_urlencode (handle->oidc->state,
1040 strlen (handle->oidc->state),
1041 &tmp);
1042 GNUNET_buffer_write_fstr (&buf,
1043 "&%s=%s",
1044 OIDC_STATE_KEY,
1045 handle->oidc->state);
1046 GNUNET_free (tmp);
1047 }
1048 if (NULL != handle->oidc->code_challenge)
1049 {
1050 GNUNET_buffer_write_fstr (&buf,
1051 "&%s=%s",
1052 OIDC_CODE_CHALLENGE_KEY,
1053 handle->oidc->code_challenge);
1054 }
1055 if (NULL != handle->oidc->nonce)
1056 {
1057 GNUNET_buffer_write_fstr (&buf,
1058 "&%s=%s",
1059 OIDC_NONCE_KEY,
1060 handle->oidc->nonce);
1061 }
1062 if (NULL != handle->oidc->claims)
1063 {
1064 GNUNET_STRINGS_urlencode (handle->oidc->claims,
1065 strlen (handle->oidc->claims),
1066 &tmp);
1067 GNUNET_buffer_write_fstr (&buf,
1068 "&%s=%s",
1069 OIDC_CLAIMS_KEY,
1070 tmp);
1071 GNUNET_free (tmp);
1072 }
1073 new_redirect = GNUNET_buffer_reap_str (&buf);
1074 resp = GNUNET_REST_create_response ("");
1075 MHD_add_response_header (resp, "Location", new_redirect);
1076 GNUNET_free (login_base_url);
1077 }
1078 else
1079 {
1080 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1081 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1082 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1083 GNUNET_SCHEDULER_add_now (&do_error, handle);
1084 return;
1085 }
1086 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1087 GNUNET_free (new_redirect);
1088 cleanup_handle (handle);
1089}
1090
1091
1092/**
1093 * Does internal server error when iteration failed.
1094 */
1095static void
1096oidc_iteration_error (void *cls)
1097{
1098 struct RequestHandle *handle = cls;
1099
1100 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1101 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1102 GNUNET_SCHEDULER_add_now (&do_error, handle);
1103}
1104
1105
1106/**
1107 * Issues ticket and redirects to relying party with the authorization code as
1108 * parameter. Otherwise redirects with error
1109 */
1110static void
1111oidc_ticket_issue_cb (void *cls,
1112 const struct GNUNET_RECLAIM_Ticket *ticket,
1113 const struct
1114 GNUNET_RECLAIM_PresentationList *presentation)
1115{
1116 struct RequestHandle *handle = cls;
1117 struct MHD_Response *resp;
1118 char *ticket_str;
1119 char *redirect_uri;
1120 char *code_string;
1121
1122 handle->idp_op = NULL;
1123 if (NULL == ticket)
1124 {
1125 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1126 handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
1127 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1128 return;
1129 }
1130 handle->ticket = *ticket;
1131 ticket_str =
1132 GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
1133 sizeof(struct GNUNET_RECLAIM_Ticket));
1134 code_string = OIDC_build_authz_code (&handle->priv_key,
1135 &handle->ticket,
1136 handle->attr_idtoken_list,
1137 presentation,
1138 handle->oidc->nonce,
1139 handle->oidc->code_challenge);
1140 if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
1141 (NULL != handle->tld))
1142 {
1143 GNUNET_asprintf (&redirect_uri,
1144 "%s.%s/%s%s%s=%s&state=%s",
1145 handle->redirect_prefix,
1146 handle->tld,
1147 handle->redirect_suffix,
1148 (NULL == strchr (handle->redirect_suffix, '?') ? "?" :
1149 "&"),
1150 handle->oidc->response_type,
1151 code_string,
1152 handle->oidc->state);
1153 }
1154 else
1155 {
1156 GNUNET_asprintf (&redirect_uri,
1157 "%s%s%s=%s&state=%s",
1158 handle->oidc->redirect_uri,
1159 (NULL == strchr (handle->oidc->redirect_uri, '?') ? "?" :
1160 "&"),
1161 handle->oidc->response_type,
1162 code_string,
1163 handle->oidc->state);
1164 }
1165 resp = GNUNET_REST_create_response ("");
1166 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1167 "Location", redirect_uri));
1168 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1169 cleanup_handle (handle);
1170 GNUNET_free (redirect_uri);
1171 GNUNET_free (ticket_str);
1172 GNUNET_free (code_string);
1173}
1174
1175
1176static struct GNUNET_RECLAIM_AttributeList*
1177attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a,
1178 struct GNUNET_RECLAIM_AttributeList *list_b)
1179{
1180 struct GNUNET_RECLAIM_AttributeList *merged_list;
1181 struct GNUNET_RECLAIM_AttributeListEntry *le_a;
1182 struct GNUNET_RECLAIM_AttributeListEntry *le_b;
1183 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1184
1185 merged_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1186 for (le_a = list_a->list_head; NULL != le_a; le_a = le_a->next)
1187 {
1188 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1189 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_a->attribute->name,
1190 &le_a->attribute->
1191 credential,
1192 le_a->attribute->type,
1193 le_a->attribute->data,
1194 le_a->attribute->data_size);
1195 le_m->attribute->id = le_a->attribute->id;
1196 le_m->attribute->flag = le_a->attribute->flag;
1197 le_m->attribute->credential = le_a->attribute->credential;
1198 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1199 merged_list->list_tail,
1200 le_m);
1201 }
1202 le_m = NULL;
1203 for (le_b = list_b->list_head; NULL != le_b; le_b = le_b->next)
1204 {
1205 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1206 {
1207 if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&le_m->attribute->id,
1208 &le_b->attribute->id))
1209 break; /** Attribute already in list **/
1210 }
1211 if (NULL != le_m)
1212 continue; /** Attribute already in list **/
1213 le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1214 le_m->attribute = GNUNET_RECLAIM_attribute_new (le_b->attribute->name,
1215 &le_b->attribute->
1216 credential,
1217 le_b->attribute->type,
1218 le_b->attribute->data,
1219 le_b->attribute->data_size);
1220 le_m->attribute->id = le_b->attribute->id;
1221 le_m->attribute->flag = le_b->attribute->flag;
1222 le_m->attribute->credential = le_b->attribute->credential;
1223 GNUNET_CONTAINER_DLL_insert (merged_list->list_head,
1224 merged_list->list_tail,
1225 le_m);
1226 }
1227 return merged_list;
1228}
1229
1230
1231static void
1232oidc_cred_collect_finished_cb (void *cls)
1233{
1234 struct RequestHandle *handle = cls;
1235 struct GNUNET_RECLAIM_AttributeList *merged_list;
1236 struct GNUNET_RECLAIM_AttributeListEntry *le_m;
1237
1238 handle->cred_it = NULL;
1239 merged_list = attribute_list_merge (handle->attr_idtoken_list,
1240 handle->attr_userinfo_list);
1241 for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next)
1242 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1243 "List Attribute in ticket to issue: %s\n",
1244 le_m->attribute->name);
1245 handle->idp_op = GNUNET_RECLAIM_ticket_issue (idp,
1246 &handle->priv_key,
1247 &handle->oidc->client_pkey,
1248 merged_list,
1249 &oidc_ticket_issue_cb,
1250 handle);
1251 GNUNET_RECLAIM_attribute_list_destroy (merged_list);
1252}
1253
1254
1255/**
1256 * Collects all attributes for an ego if in scope parameter
1257 */
1258static void
1259oidc_cred_collect (void *cls,
1260 const struct GNUNET_CRYPTO_PublicKey *identity,
1261 const struct GNUNET_RECLAIM_Credential *cred)
1262{
1263 struct RequestHandle *handle = cls;
1264 struct GNUNET_RECLAIM_AttributeListEntry *le;
1265 struct GNUNET_RECLAIM_CredentialListEntry *ale;
1266
1267 for (ale = handle->credentials->list_head; NULL != ale; ale = ale->next)
1268 {
1269 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->credential->id,
1270 &cred->id))
1271 continue;
1272 /** Credential already in list **/
1273 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1274 return;
1275 }
1276
1277 for (le = handle->attr_idtoken_list->list_head; NULL != le; le = le->next)
1278 {
1279 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->credential,
1280 &cred->id))
1281 continue;
1282 /** Credential matches for attribute, add **/
1283 ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry);
1284 ale->credential = GNUNET_RECLAIM_credential_new (cred->name,
1285 cred->type,
1286 cred->data,
1287 cred->data_size);
1288 GNUNET_CONTAINER_DLL_insert (handle->credentials->list_head,
1289 handle->credentials->list_tail,
1290 ale);
1291 }
1292 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
1293}
1294
1295
1296static void
1297oidc_attr_collect_finished_cb (void *cls)
1298{
1299 struct RequestHandle *handle = cls;
1300
1301 handle->attr_it = NULL;
1302 handle->ticket_it = NULL;
1303 if (NULL == handle->attr_idtoken_list->list_head)
1304 {
1305 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1306 handle->edesc = GNUNET_strdup ("The requested scope is not available.");
1307 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1308 return;
1309 }
1310 handle->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList);
1311 handle->cred_it =
1312 GNUNET_RECLAIM_get_credentials_start (idp,
1313 &handle->priv_key,
1314 &oidc_iteration_error,
1315 handle,
1316 &oidc_cred_collect,
1317 handle,
1318 &oidc_cred_collect_finished_cb,
1319 handle);
1320
1321}
1322
1323
1324static int
1325attr_in_claims_request (struct RequestHandle *handle,
1326 const char *attr_name,
1327 const char *claims_parameter)
1328{
1329 int ret = GNUNET_NO;
1330 json_t *root;
1331 json_error_t error;
1332 json_t *claims_j;
1333 const char *key;
1334 json_t *value;
1335
1336 /** Check if attribute is requested through a scope **/
1337 if (GNUNET_YES == OIDC_check_scopes_for_claim_request (handle->oidc->scope,
1338 attr_name))
1339 return GNUNET_YES;
1340
1341 /** Try claims parameter if not in scope */
1342 if (NULL != handle->oidc->claims)
1343 {
1344 root = json_loads (handle->oidc->claims, JSON_DECODE_ANY, &error);
1345 claims_j = json_object_get (root, claims_parameter);
1346 /* obj is a JSON object */
1347 if (NULL != claims_j)
1348 {
1349 json_object_foreach (claims_j, key, value) {
1350 if (0 != strcmp (attr_name, key))
1351 continue;
1352 ret = GNUNET_YES;
1353 break;
1354 }
1355 }
1356 json_decref (root);
1357 }
1358 return ret;
1359}
1360
1361
1362static int
1363attr_in_idtoken_request (struct RequestHandle *handle,
1364 const char *attr_name)
1365{
1366 return attr_in_claims_request (handle, attr_name, "id_token");
1367}
1368
1369
1370static int
1371attr_in_userinfo_request (struct RequestHandle *handle,
1372 const char *attr_name)
1373{
1374 return attr_in_claims_request (handle, attr_name, "userinfo");
1375}
1376
1377
1378/**
1379 * Collects all attributes for an ego if in scope parameter
1380 */
1381static void
1382oidc_attr_collect (void *cls,
1383 const struct GNUNET_CRYPTO_PublicKey *identity,
1384 const struct GNUNET_RECLAIM_Attribute *attr)
1385{
1386 struct RequestHandle *handle = cls;
1387 struct GNUNET_RECLAIM_AttributeListEntry *le;
1388 if (GNUNET_YES == attr_in_idtoken_request (handle, attr->name))
1389 {
1390 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1391 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1392 &attr->credential,
1393 attr->type,
1394 attr->data,
1395 attr->data_size);
1396 le->attribute->id = attr->id;
1397 le->attribute->flag = attr->flag;
1398 le->attribute->credential = attr->credential;
1399 GNUNET_CONTAINER_DLL_insert (handle->attr_idtoken_list->list_head,
1400 handle->attr_idtoken_list->list_tail,
1401 le);
1402 }
1403 if (GNUNET_YES == attr_in_userinfo_request (handle, attr->name))
1404 {
1405 le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1406 le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1407 &attr->credential,
1408 attr->type,
1409 attr->data,
1410 attr->data_size);
1411 le->attribute->id = attr->id;
1412 le->attribute->flag = attr->flag;
1413 le->attribute->credential = attr->credential;
1414 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
1415 handle->attr_userinfo_list->list_tail,
1416 le);
1417 }
1418
1419 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1420}
1421
1422
1423/**
1424 * Checks time and cookie and redirects accordingly
1425 */
1426static void
1427code_redirect (void *cls)
1428{
1429 struct RequestHandle *handle = cls;
1430 struct GNUNET_TIME_Absolute current_time;
1431 struct GNUNET_TIME_Absolute *relog_time;
1432 struct GNUNET_CRYPTO_PublicKey pubkey;
1433 struct GNUNET_CRYPTO_PublicKey ego_pkey;
1434 struct GNUNET_HashCode cache_key;
1435 char *identity_cookie;
1436
1437 GNUNET_asprintf (&identity_cookie,
1438 "Identity=%s",
1439 handle->oidc->login_identity);
1440 GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1441 GNUNET_free (identity_cookie);
1442 // No login time for identity -> redirect to login
1443 if (GNUNET_YES ==
1444 GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1445 {
1446 relog_time =
1447 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1448 current_time = GNUNET_TIME_absolute_get ();
1449 // 30 min after old login -> redirect to login
1450 if (current_time.abs_value_us <= relog_time->abs_value_us)
1451 {
1452 if (GNUNET_OK !=
1453 GNUNET_CRYPTO_public_key_from_string (handle->oidc
1454 ->login_identity,
1455 &pubkey))
1456 {
1457 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1458 handle->edesc =
1459 GNUNET_strdup ("The cookie of a login identity is not valid");
1460 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1461 return;
1462 }
1463 // iterate over egos and compare their public key
1464 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
1465 handle->ego_entry = handle->ego_entry->next)
1466 {
1467 GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1468 if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1469 {
1470 handle->priv_key =
1471 *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1472 handle->attr_idtoken_list =
1473 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1474 handle->attr_userinfo_list =
1475 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1476 handle->attr_it =
1477 GNUNET_RECLAIM_get_attributes_start (idp,
1478 &handle->priv_key,
1479 &oidc_iteration_error,
1480 handle,
1481 &oidc_attr_collect,
1482 handle,
1483 &oidc_attr_collect_finished_cb,
1484 handle);
1485 return;
1486 }
1487 }
1488 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1489 return;
1490 }
1491 }
1492}
1493
1494
1495static void
1496build_redirect (void *cls)
1497{
1498 struct RequestHandle *handle = cls;
1499 struct MHD_Response *resp;
1500 char *redirect_uri;
1501
1502 if (GNUNET_YES == handle->oidc->user_cancelled)
1503 {
1504 if ((NULL != handle->redirect_prefix) &&
1505 (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1506 {
1507 GNUNET_asprintf (&redirect_uri,
1508 "%s.%s/%s?error=%s&error_description=%s&state=%s",
1509 handle->redirect_prefix,
1510 handle->tld,
1511 handle->redirect_suffix,
1512 "access_denied",
1513 "User denied access",
1514 handle->oidc->state);
1515 }
1516 else
1517 {
1518 GNUNET_asprintf (&redirect_uri,
1519 "%s?error=%s&error_description=%s&state=%s",
1520 handle->oidc->redirect_uri,
1521 "access_denied",
1522 "User denied access",
1523 handle->oidc->state);
1524 }
1525 resp = GNUNET_REST_create_response ("");
1526 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1527 "Location",
1528 redirect_uri));
1529 handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1530 cleanup_handle (handle);
1531 GNUNET_free (redirect_uri);
1532 return;
1533 }
1534 GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1535}
1536
1537
1538static void
1539lookup_redirect_uri_result (void *cls,
1540 uint32_t rd_count,
1541 const struct GNUNET_GNSRECORD_Data *rd)
1542{
1543 struct RequestHandle *handle = cls;
1544 char *tmp;
1545 char *tmp_key_str;
1546 char *pos;
1547 struct GNUNET_CRYPTO_PublicKey redirect_zone;
1548
1549 handle->gns_op = NULL;
1550 if (0 == rd_count)
1551 {
1552 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1553 handle->edesc =
1554 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1555 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1556 return;
1557 }
1558 for (int i = 0; i < rd_count; i++)
1559 {
1560 if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1561 continue;
1562 if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1563 continue;
1564 tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1565 if (NULL == strstr (tmp, handle->oidc->client_id))
1566 {
1567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1568 "Redirect uri %s does not contain client_id %s\n",
1569 tmp,
1570 handle->oidc->client_id);
1571 }
1572 else
1573 {
1574 pos = strrchr (tmp, (unsigned char) '.');
1575 if (NULL == pos)
1576 {
1577 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1578 "Redirect uri %s contains client_id but is malformed\n",
1579 tmp);
1580 GNUNET_free (tmp);
1581 continue;
1582 }
1583 *pos = '\0';
1584 handle->redirect_prefix = GNUNET_strdup (tmp);
1585 tmp_key_str = pos + 1;
1586 pos = strchr (tmp_key_str, (unsigned char) '/');
1587 if (NULL == pos)
1588 {
1589 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1590 "Redirect uri %s contains client_id but is malformed\n",
1591 tmp);
1592 GNUNET_free (tmp);
1593 continue;
1594 }
1595 *pos = '\0';
1596 handle->redirect_suffix = GNUNET_strdup (pos + 1);
1597
1598 GNUNET_STRINGS_string_to_data (tmp_key_str,
1599 strlen (tmp_key_str),
1600 &redirect_zone,
1601 sizeof(redirect_zone));
1602 }
1603 GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1604 GNUNET_free (tmp);
1605 return;
1606 }
1607 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1608 handle->edesc =
1609 GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1610 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1611}
1612
1613
1614/**
1615 * Initiate redirect back to client.
1616 */
1617static void
1618client_redirect (void *cls)
1619{
1620 struct RequestHandle *handle = cls;
1621
1622 /* Lookup client redirect uri to verify request */
1623 handle->gns_op =
1624 GNUNET_GNS_lookup (gns_handle,
1625 GNUNET_GNS_EMPTY_LABEL_AT,
1626 &handle->oidc->client_pkey,
1627 GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1628 GNUNET_GNS_LO_DEFAULT,
1629 &lookup_redirect_uri_result,
1630 handle);
1631}
1632
1633
1634static char *
1635get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1636{
1637 struct GNUNET_HashCode hc;
1638 char *value;
1639 char *res;
1640
1641 GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1642 if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1643 ->url_param_map,
1644 &hc))
1645 return NULL;
1646 value =
1647 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1648 if (NULL == value)
1649 return NULL;
1650 GNUNET_STRINGS_urldecode (value, strlen (value), &res);
1651 return res;
1652}
1653
1654
1655/**
1656 * Iteration over all results finished, build final
1657 * response.
1658 *
1659 * @param cls the `struct RequestHandle`
1660 */
1661static void
1662build_authz_response (void *cls)
1663{
1664 struct RequestHandle *handle = cls;
1665 struct GNUNET_HashCode cache_key;
1666
1667 char *expected_scope;
1668 char delimiter[] = " ";
1669 int number_of_ignored_parameter, iterator;
1670
1671
1672 // REQUIRED value: redirect_uri
1673 handle->oidc->redirect_uri =
1674 get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1675 if (NULL == handle->oidc->redirect_uri)
1676 {
1677 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1678 handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1679 GNUNET_SCHEDULER_add_now (&do_error, handle);
1680 return;
1681 }
1682
1683 // REQUIRED value: response_type
1684 handle->oidc->response_type =
1685 get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1686 if (NULL == handle->oidc->response_type)
1687 {
1688 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1689 handle->edesc = GNUNET_strdup ("missing parameter response_type");
1690 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1691 return;
1692 }
1693
1694 // REQUIRED value: scope
1695 handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1696 if (NULL == handle->oidc->scope)
1697 {
1698 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1699 handle->edesc = GNUNET_strdup ("missing parameter scope");
1700 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1701 return;
1702 }
1703
1704 // OPTIONAL value: nonce
1705 handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1706
1707 // OPTIONAL value: claims
1708 handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1709
1710 // TODO check other values if needed
1711 number_of_ignored_parameter =
1712 sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1713 for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1714 {
1715 GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1716 strlen (OIDC_ignored_parameter_array[iterator]),
1717 &cache_key);
1718 if (GNUNET_YES ==
1719 GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1720 ->url_param_map,
1721 &cache_key))
1722 {
1723 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1724 GNUNET_asprintf (&handle->edesc,
1725 "Server will not handle parameter: %s",
1726 OIDC_ignored_parameter_array[iterator]);
1727 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1728 return;
1729 }
1730 }
1731
1732 // We only support authorization code flows.
1733 if (0 != strcmp (handle->oidc->response_type,
1734 OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1735 {
1736 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1737 handle->edesc = GNUNET_strdup ("The authorization server does not support "
1738 "obtaining this authorization code.");
1739 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1740 return;
1741 }
1742
1743 // Checks if scope contains 'openid'
1744 expected_scope = GNUNET_strdup (handle->oidc->scope);
1745 char *test;
1746 test = strtok (expected_scope, delimiter);
1747 while (NULL != test)
1748 {
1749 if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1750 break;
1751 test = strtok (NULL, delimiter);
1752 }
1753 if (NULL == test)
1754 {
1755 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1756 handle->edesc =
1757 GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1758 GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1759 GNUNET_free (expected_scope);
1760 return;
1761 }
1762
1763 GNUNET_free (expected_scope);
1764 if ((NULL == handle->oidc->login_identity) &&
1765 (GNUNET_NO == handle->oidc->user_cancelled))
1766 GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1767 else
1768 GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1769}
1770
1771
1772/**
1773 * Iterate over tlds in config
1774 */
1775static void
1776tld_iter (void *cls, const char *section, const char *option, const char *value)
1777{
1778 struct RequestHandle *handle = cls;
1779 struct GNUNET_CRYPTO_PublicKey pkey;
1780
1781 if (GNUNET_OK !=
1782 GNUNET_CRYPTO_public_key_from_string (value, &pkey))
1783 {
1784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1785 return;
1786 }
1787 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1788 handle->tld = GNUNET_strdup (option + 1);
1789}
1790
1791
1792/**
1793 * Responds to authorization GET and url-encoded POST request
1794 *
1795 * @param con_handle the connection handle
1796 * @param url the url
1797 * @param cls the RequestHandle
1798 */
1799static void
1800authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1801 const char *url,
1802 void *cls)
1803{
1804 struct RequestHandle *handle = cls;
1805 struct EgoEntry *tmp_ego;
1806 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
1807 struct GNUNET_CRYPTO_PublicKey pkey;
1808
1809 cookie_identity_interpretation (handle);
1810
1811 // RECOMMENDED value: state - REQUIRED for answers
1812 handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1813
1814 // REQUIRED value: client_id
1815 handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1816 if (NULL == handle->oidc->client_id)
1817 {
1818 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1819 handle->edesc = GNUNET_strdup ("missing parameter client_id");
1820 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1821 GNUNET_SCHEDULER_add_now (&do_error, handle);
1822 return;
1823 }
1824
1825 // OPTIONAL value: code_challenge
1826 handle->oidc->code_challenge = get_url_parameter_copy (handle,
1827 OIDC_CODE_CHALLENGE_KEY);
1828 if (NULL == handle->oidc->code_challenge)
1829 {
1830 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1831 "OAuth authorization request does not contain PKCE parameters!\n");
1832 }
1833
1834 if (GNUNET_OK !=
1835 GNUNET_CRYPTO_public_key_from_string (handle->oidc->client_id,
1836 &handle->oidc->client_pkey))
1837 {
1838 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1839 handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1840 "authorization code using this method.");
1841 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1842 GNUNET_SCHEDULER_add_now (&do_error, handle);
1843 return;
1844 }
1845
1846 // If we know this identity, translated the corresponding TLD
1847 // TODO: We might want to have a reverse lookup functionality for TLDs?
1848 for (tmp_ego = ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1849 {
1850 priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1851 GNUNET_CRYPTO_key_get_public (priv_key, &pkey);
1852 if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1853 {
1854 handle->tld = GNUNET_strdup (tmp_ego->identifier);
1855 handle->ego_entry = ego_tail;
1856 }
1857 }
1858 if (NULL == handle->tld)
1859 GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1860 if (NULL == handle->tld)
1861 handle->tld = GNUNET_strdup (handle->oidc->client_id);
1862 GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1863}
1864
1865
1866/**
1867 * Combines an identity with a login time and responds OK to login request
1868 *
1869 * @param con_handle the connection handle
1870 * @param url the url
1871 * @param cls the RequestHandle
1872 */
1873static void
1874login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1875 const char *url,
1876 void *cls)
1877{
1878 struct MHD_Response *resp = GNUNET_REST_create_response ("");
1879 struct RequestHandle *handle = cls;
1880 struct GNUNET_HashCode cache_key;
1881 struct GNUNET_TIME_Absolute *current_time;
1882 struct GNUNET_TIME_Absolute *last_time;
1883 char *cookie;
1884 char *header_val;
1885 json_t *root;
1886 json_error_t error;
1887 json_t *identity;
1888 char term_data[handle->rest_handle->data_size + 1];
1889
1890 term_data[handle->rest_handle->data_size] = '\0';
1891 GNUNET_memcpy (term_data,
1892 handle->rest_handle->data,
1893 handle->rest_handle->data_size);
1894 root = json_loads (term_data, JSON_DECODE_ANY, &error);
1895 identity = json_object_get (root, "identity");
1896 if (! json_is_string (identity))
1897 {
1898 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1899 "Error parsing json string from %s\n",
1900 term_data);
1901 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1902 json_decref (root);
1903 cleanup_handle (handle);
1904 return;
1905 }
1906 GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1907 GNUNET_asprintf (&header_val,
1908 "%s;Max-Age=%d",
1909 cookie,
1910 OIDC_COOKIE_EXPIRATION);
1911 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
1912 "Set-Cookie", header_val));
1913 GNUNET_assert (MHD_NO !=
1914 MHD_add_response_header (resp,
1915 "Access-Control-Allow-Methods",
1916 "POST"));
1917 GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1918
1919 if (0 != strcmp (json_string_value (identity), "Denied"))
1920 {
1921 current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1922 *current_time = GNUNET_TIME_relative_to_absolute (
1923 GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1924 OIDC_COOKIE_EXPIRATION));
1925 last_time =
1926 GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1927 GNUNET_free (last_time);
1928 GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1929 &cache_key,
1930 current_time,
1931 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1932 }
1933 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1934 GNUNET_free (cookie);
1935 GNUNET_free (header_val);
1936 json_decref (root);
1937 cleanup_handle (handle);
1938}
1939
1940
1941static int
1942parse_credentials_basic_auth (struct RequestHandle *handle,
1943 char **client_id,
1944 char **client_secret)
1945{
1946 struct GNUNET_HashCode cache_key;
1947 char *authorization;
1948 char *credentials;
1949 char *basic_authorization;
1950 char *client_id_tmp;
1951 char *pass;
1952
1953 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1954 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1955 &cache_key);
1956 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1957 ->header_param_map,
1958 &cache_key))
1959 return GNUNET_SYSERR;
1960 authorization =
1961 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1962 &cache_key);
1963
1964 // split header in "Basic" and [content]
1965 credentials = strtok (authorization, " ");
1966 if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1967 return GNUNET_SYSERR;
1968 credentials = strtok (NULL, " ");
1969 if (NULL == credentials)
1970 return GNUNET_SYSERR;
1971 GNUNET_STRINGS_base64_decode (credentials,
1972 strlen (credentials),
1973 (void **) &basic_authorization);
1974
1975 if (NULL == basic_authorization)
1976 return GNUNET_SYSERR;
1977 client_id_tmp = strtok (basic_authorization, ":");
1978 if (NULL == client_id_tmp)
1979 {
1980 GNUNET_free (basic_authorization);
1981 return GNUNET_SYSERR;
1982 }
1983 pass = strtok (NULL, ":");
1984 if (NULL == pass)
1985 {
1986 GNUNET_free (basic_authorization);
1987 return GNUNET_SYSERR;
1988 }
1989 *client_id = strdup (client_id_tmp);
1990 *client_secret = strdup (pass);
1991 GNUNET_free (basic_authorization);
1992 return GNUNET_OK;
1993}
1994
1995
1996static int
1997parse_credentials_post_body (struct RequestHandle *handle,
1998 char **client_id,
1999 char **client_secret)
2000{
2001 struct GNUNET_HashCode cache_key;
2002 char *client_id_tmp;
2003 char *pass;
2004
2005 GNUNET_CRYPTO_hash ("client_id",
2006 strlen ("client_id"),
2007 &cache_key);
2008 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2009 ->url_param_map,
2010 &cache_key))
2011 return GNUNET_SYSERR;
2012 client_id_tmp = GNUNET_CONTAINER_multihashmap_get (
2013 handle->rest_handle->url_param_map,
2014 &cache_key);
2015 if (NULL == client_id_tmp)
2016 return GNUNET_SYSERR;
2017 *client_id = strdup (client_id_tmp);
2018 GNUNET_CRYPTO_hash ("client_secret",
2019 strlen ("client_secret"),
2020 &cache_key);
2021 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2022 ->url_param_map,
2023 &cache_key))
2024 {
2025 GNUNET_free (*client_id);
2026 *client_id = NULL;
2027 return GNUNET_SYSERR;
2028 }
2029 pass = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
2030 &cache_key);
2031 if (NULL == pass)
2032 {
2033 GNUNET_free (*client_id);
2034 *client_id = NULL;
2035 return GNUNET_SYSERR;
2036 }
2037 *client_secret = strdup (pass);
2038 return GNUNET_OK;
2039}
2040
2041
2042static int
2043check_authorization (struct RequestHandle *handle,
2044 struct GNUNET_CRYPTO_PublicKey *cid)
2045{
2046 char *expected_pass;
2047 char *received_cid;
2048 char *received_cpw;
2049 char *pkce_cv;
2050
2051 if (GNUNET_OK == parse_credentials_basic_auth (handle,
2052 &received_cid,
2053 &received_cpw))
2054 {
2055 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2056 "Received client credentials in HTTP AuthZ header\n");
2057 }
2058 else if (GNUNET_OK == parse_credentials_post_body (handle,
2059 &received_cid,
2060 &received_cpw))
2061 {
2062 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2063 "Received client credentials in POST body\n");
2064 }
2065 else
2066 {
2067 /** Allow public clients with PKCE **/
2068 pkce_cv = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2069 if (NULL == pkce_cv)
2070 {
2071 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2072 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2073 return GNUNET_SYSERR;
2074 }
2075 handle->public_client = GNUNET_YES;
2076 GNUNET_free (pkce_cv);
2077 received_cid = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
2078 GNUNET_STRINGS_string_to_data (received_cid,
2079 strlen (received_cid),
2080 cid,
2081 sizeof(struct GNUNET_CRYPTO_PublicKey));
2082 GNUNET_free (received_cid);
2083 return GNUNET_OK;
2084
2085 }
2086
2087 // check client password
2088 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
2089 "reclaim-rest-plugin",
2090 "OIDC_CLIENT_HMAC_SECRET",
2091 &expected_pass))
2092 {
2093 if (0 != strcmp (expected_pass, received_cpw))
2094 {
2095 GNUNET_free (expected_pass);
2096 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2097 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2098 GNUNET_free (received_cpw);
2099 GNUNET_free (received_cid);
2100 return GNUNET_SYSERR;
2101 }
2102 GNUNET_free (expected_pass);
2103 }
2104 else
2105 {
2106 GNUNET_free (received_cpw);
2107 GNUNET_free (received_cid);
2108 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2109 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2110 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2111 return GNUNET_SYSERR;
2112 }
2113 // check client_id
2114 for (handle->ego_entry = ego_head; NULL != handle->ego_entry;
2115 handle->ego_entry = handle->ego_entry->next)
2116 {
2117 if (0 == strcmp (handle->ego_entry->keystring, received_cid))
2118 break;
2119 }
2120 if (NULL == handle->ego_entry)
2121 {
2122 GNUNET_free (received_cpw);
2123 GNUNET_free (received_cid);
2124 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
2125 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2126 return GNUNET_SYSERR;
2127 }
2128 GNUNET_STRINGS_string_to_data (received_cid,
2129 strlen (received_cid),
2130 cid,
2131 sizeof(struct GNUNET_CRYPTO_PublicKey));
2132
2133 GNUNET_free (received_cpw);
2134 GNUNET_free (received_cid);
2135 return GNUNET_OK;
2136}
2137
2138
2139const struct EgoEntry *
2140find_ego (struct RequestHandle *handle,
2141 struct GNUNET_CRYPTO_PublicKey *test_key)
2142{
2143 struct EgoEntry *ego_entry;
2144 struct GNUNET_CRYPTO_PublicKey pub_key;
2145
2146 for (ego_entry = ego_head; NULL != ego_entry;
2147 ego_entry = ego_entry->next)
2148 {
2149 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
2150 if (0 == GNUNET_memcmp (&pub_key, test_key))
2151 return ego_entry;
2152 }
2153 return NULL;
2154}
2155
2156
2157/**
2158 * Responds to token url-encoded POST request
2159 *
2160 * @param con_handle the connection handle
2161 * @param url the url
2162 * @param cls the RequestHandle
2163 */
2164static void
2165token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2166 const char *url,
2167 void *cls)
2168{
2169 struct RequestHandle *handle = cls;
2170 const struct EgoEntry *ego_entry = NULL;
2171 struct GNUNET_TIME_Relative expiration_time;
2172 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2173 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2174 struct GNUNET_RECLAIM_Ticket ticket;
2175 struct GNUNET_CRYPTO_PublicKey cid;
2176 struct GNUNET_HashCode cache_key;
2177 struct MHD_Response *resp = NULL;
2178 char *grant_type = NULL;
2179 char *code = NULL;
2180 char *json_response = NULL;
2181 char *id_token = NULL;
2182 char *access_token = NULL;
2183 char *jwa = NULL;
2184 char *jwt_secret = NULL;
2185 char *nonce = NULL;
2186 char *code_verifier = NULL;
2187 json_t *oidc_jwk = NULL;
2188 char *oidc_jwk_path = NULL;
2189 char *oidc_directory = NULL;
2190 char *tmp_at = NULL;
2191
2192 /*
2193 * Check Authorization
2194 */
2195 if (GNUNET_SYSERR == check_authorization (handle, &cid))
2196 {
2197 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2198 "OIDC authorization for token endpoint failed\n");
2199 GNUNET_SCHEDULER_add_now (&do_error, handle);
2200 return;
2201 }
2202
2203 /*
2204 * Check parameter
2205 */
2206
2207 // TODO Do not allow multiple equal parameter names
2208 // REQUIRED grant_type
2209 GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
2210 strlen (OIDC_GRANT_TYPE_KEY),
2211 &cache_key);
2212 grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
2213 if (NULL == grant_type)
2214 {
2215 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2216 handle->edesc = GNUNET_strdup ("missing parameter grant_type");
2217 handle->response_code = MHD_HTTP_BAD_REQUEST;
2218 GNUNET_SCHEDULER_add_now (&do_error, handle);
2219 return;
2220 }
2221
2222 // Check parameter grant_type == "authorization_code"
2223 if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
2224 {
2225 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
2226 handle->response_code = MHD_HTTP_BAD_REQUEST;
2227 GNUNET_free (grant_type);
2228 GNUNET_SCHEDULER_add_now (&do_error, handle);
2229 return;
2230 }
2231 GNUNET_free (grant_type);
2232 // REQUIRED code
2233 code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
2234 if (NULL == code)
2235 {
2236 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2237 handle->edesc = GNUNET_strdup ("missing parameter code");
2238 handle->response_code = MHD_HTTP_BAD_REQUEST;
2239 GNUNET_SCHEDULER_add_now (&do_error, handle);
2240 return;
2241 }
2242 ego_entry = find_ego (handle, &cid);
2243 if (NULL == ego_entry)
2244 {
2245 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2246 handle->edesc = GNUNET_strdup ("Unknown client");
2247 handle->response_code = MHD_HTTP_BAD_REQUEST;
2248 GNUNET_free (code);
2249 GNUNET_SCHEDULER_add_now (&do_error, handle);
2250 return;
2251 }
2252
2253 // REQUIRED code verifier
2254 code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
2255 if (NULL == code_verifier)
2256 {
2257 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2258 "OAuth authorization request does not contain PKCE parameters!\n");
2259
2260 }
2261
2262 // decode code
2263 if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket,
2264 &cl, &pl, &nonce,
2265 OIDC_VERIFICATION_DEFAULT))
2266 {
2267 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2268 handle->edesc = GNUNET_strdup ("invalid code");
2269 handle->response_code = MHD_HTTP_BAD_REQUEST;
2270 GNUNET_free (code);
2271 if (NULL != code_verifier)
2272 GNUNET_free (code_verifier);
2273 GNUNET_SCHEDULER_add_now (&do_error, handle);
2274 return;
2275 }
2276 if (NULL != code_verifier)
2277 GNUNET_free (code_verifier);
2278
2279 // create jwt
2280 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
2281 "reclaim-rest-plugin",
2282 "expiration_time",
2283 &expiration_time))
2284 {
2285 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
2286 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
2287 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2288 GNUNET_free (code);
2289 if (NULL != nonce)
2290 GNUNET_free (nonce);
2291 GNUNET_RECLAIM_attribute_list_destroy (cl);
2292 GNUNET_RECLAIM_presentation_list_destroy (pl);
2293 GNUNET_SCHEDULER_add_now (&do_error, handle);
2294 return;
2295 }
2296
2297 // Check if HMAC or RSA should be used
2298 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2299 "reclaim-rest-plugin",
2300 "oidc_json_web_algorithm",
2301 &jwa))
2302 {
2303 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2304 "Could not read OIDC JSON Web Algorithm config attribute."
2305 "Defaulting to RS256.");
2306 jwa = JWT_ALG_VALUE_RSA;
2307 }
2308
2309 if ( ! strcmp (jwa, JWT_ALG_VALUE_RSA))
2310 {
2311 // Replace for now
2312 oidc_jwk_path = get_oidc_jwk_path (cls);
2313 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2314
2315 // Check if secret JWK exists
2316 if (! oidc_jwk)
2317 {
2318 // Generate and save a new key
2319 oidc_jwk = generate_jwk ();
2320 oidc_directory = get_oidc_dir_path (cls);
2321
2322 // Create new oidc directory
2323 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2324 {
2325 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2326 ("Failed to create directory `%s' for storing oidc data\n"),
2327 oidc_directory);
2328 }
2329 else
2330 {
2331 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2332 }
2333 }
2334
2335 // Generate oidc token
2336 id_token = OIDC_generate_id_token_rsa (&ticket.audience,
2337 &ticket.identity,
2338 cl,
2339 pl,
2340 &expiration_time,
2341 (NULL != nonce) ? nonce : NULL,
2342 oidc_jwk);
2343 }
2344 else if ( ! strcmp (jwa, JWT_ALG_VALUE_HMAC))
2345 {
2346 // TODO OPTIONAL acr,amr,azp
2347 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2348 "reclaim-rest-plugin",
2349 "jwt_secret",
2350 &jwt_secret))
2351 {
2352 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2353 handle->edesc = GNUNET_strdup ("No signing secret configured!");
2354 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2355 GNUNET_free (code);
2356 GNUNET_RECLAIM_attribute_list_destroy (cl);
2357 GNUNET_RECLAIM_presentation_list_destroy (pl);
2358 if (NULL != nonce)
2359 GNUNET_free (nonce);
2360 GNUNET_SCHEDULER_add_now (&do_error, handle);
2361 return;
2362 }
2363
2364 id_token = OIDC_generate_id_token_hmac (&ticket.audience,
2365 &ticket.identity,
2366 cl,
2367 pl,
2368 &expiration_time,
2369 (NULL != nonce) ? nonce : NULL,
2370 jwt_secret);
2371
2372 GNUNET_free (jwt_secret);
2373 }
2374 else
2375 {
2376 // TODO: OPTION NOT FOUND ERROR
2377 }
2378
2379 if (NULL != nonce)
2380 GNUNET_free (nonce);
2381 access_token = OIDC_access_token_new (&ticket);
2382 /**
2383 * Store mapping from access token to code so we can later
2384 * fall back on the provided attributes in userinfo one time.
2385 */
2386 GNUNET_CRYPTO_hash (access_token,
2387 strlen (access_token),
2388 &cache_key);
2389 /**
2390 * Note to future self: This cache has the following purpose:
2391 * Some OIDC plugins call the userendpoint right after receiving an
2392 * ID token and access token. There are reasons why this would make sense.
2393 * Others not so much.
2394 * In any case, in order to smoothen out the user experience upon login
2395 * (authorization), we speculatively cache the next
2396 * userinfo response in case the actual resolution through reclaim/GNS
2397 * takes too long.
2398 */
2399 tmp_at = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2400 &cache_key);
2401 GNUNET_CONTAINER_multihashmap_put (oidc_code_cache,
2402 &cache_key,
2403 code,
2404 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
2405 /* If there was a previous code in there, free the old value */
2406 if (NULL != tmp_at)
2407 {
2408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2409 "OIDC access token already issued. Cleanup.\n");
2410 GNUNET_free (tmp_at);
2411 }
2412
2413 OIDC_build_token_response (access_token,
2414 id_token,
2415 &expiration_time,
2416 &json_response);
2417
2418 resp = GNUNET_REST_create_response (json_response);
2419 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2420 "Cache-Control",
2421 "no-store"));
2422 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2423 "Pragma", "no-cache"));
2424 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
2425 "Content-Type",
2426 "application/json"));
2427 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2428 GNUNET_RECLAIM_attribute_list_destroy (cl);
2429 GNUNET_RECLAIM_presentation_list_destroy (pl);
2430 GNUNET_free (access_token);
2431 GNUNET_free (json_response);
2432 GNUNET_free (id_token);
2433 cleanup_handle (handle);
2434}
2435
2436
2437/**
2438 * Collects claims and stores them in handle
2439 */
2440static void
2441consume_ticket (void *cls,
2442 const struct GNUNET_CRYPTO_PublicKey *identity,
2443 const struct GNUNET_RECLAIM_Attribute *attr,
2444 const struct GNUNET_RECLAIM_Presentation *presentation)
2445{
2446 struct RequestHandle *handle = cls;
2447 struct GNUNET_RECLAIM_AttributeListEntry *ale;
2448 struct GNUNET_RECLAIM_PresentationListEntry *atle;
2449 struct MHD_Response *resp;
2450 struct GNUNET_HashCode cache_key;
2451 char *result_str;
2452 char *cached_code;
2453
2454 if (NULL != handle->consume_timeout_op)
2455 GNUNET_SCHEDULER_cancel (handle->consume_timeout_op);
2456 handle->consume_timeout_op = NULL;
2457 handle->idp_op = NULL;
2458
2459 /**
2460 * We received a reply. In any case clear the cache.
2461 */
2462 GNUNET_CRYPTO_hash (handle->access_token,
2463 strlen (handle->access_token),
2464 &cache_key);
2465 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2466 &cache_key);
2467 if (NULL != cached_code)
2468 {
2469 GNUNET_assert (GNUNET_YES ==
2470 GNUNET_CONTAINER_multihashmap_remove (oidc_code_cache,
2471 &cache_key,
2472 cached_code));
2473 GNUNET_free (cached_code);
2474 }
2475
2476
2477 if (NULL == identity)
2478 {
2479 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2480 handle->attr_userinfo_list,
2481 handle->presentations);
2482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2483 resp = GNUNET_REST_create_response (result_str);
2484 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2485 GNUNET_free (result_str);
2486 cleanup_handle (handle);
2487 return;
2488 }
2489 ale = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
2490 ale->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
2491 &attr->credential,
2492 attr->type,
2493 attr->data,
2494 attr->data_size);
2495 ale->attribute->id = attr->id;
2496 ale->attribute->flag = attr->flag;
2497 ale->attribute->credential = attr->credential;
2498 GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head,
2499 handle->attr_userinfo_list->list_tail,
2500 ale);
2501 if (NULL == presentation)
2502 return;
2503 for (atle = handle->presentations->list_head;
2504 NULL != atle; atle = atle->next)
2505 {
2506 if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (
2507 &atle->presentation->credential_id,
2508 &presentation->credential_id))
2509 continue;
2510 break; /** already in list **/
2511 }
2512 if (NULL == atle)
2513 {
2514 /** Credential matches for attribute, add **/
2515 atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry);
2516 atle->presentation = GNUNET_RECLAIM_presentation_new (presentation->type,
2517 presentation->data,
2518 presentation->
2519 data_size);
2520 atle->presentation->credential_id = presentation->credential_id;
2521 GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head,
2522 handle->presentations->list_tail,
2523 atle);
2524 }
2525}
2526
2527
2528static void
2529consume_fail (void *cls)
2530{
2531 struct RequestHandle *handle = cls;
2532 struct GNUNET_HashCode cache_key;
2533 struct GNUNET_RECLAIM_AttributeList *cl = NULL;
2534 struct GNUNET_RECLAIM_PresentationList *pl = NULL;
2535 struct GNUNET_RECLAIM_Ticket ticket;
2536 struct MHD_Response *resp;
2537 char *nonce;
2538 char *cached_code;
2539 char *result_str;
2540
2541
2542 handle->consume_timeout_op = NULL;
2543 if (NULL != handle->idp_op)
2544 GNUNET_RECLAIM_cancel (handle->idp_op);
2545 handle->idp_op = NULL;
2546
2547 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2548 "Ticket consumptioned timed out. Using cache...\n");
2549 GNUNET_CRYPTO_hash (handle->access_token,
2550 strlen (handle->access_token),
2551 &cache_key);
2552 cached_code = GNUNET_CONTAINER_multihashmap_get (oidc_code_cache,
2553 &cache_key);
2554 if (NULL == cached_code)
2555 {
2556 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2557 handle->edesc = GNUNET_strdup ("No Access Token in cache!");
2558 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2559 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2560 return;
2561 }
2562 /**
2563 * Remove the cached item
2564 */
2565 GNUNET_assert (GNUNET_YES ==
2566 GNUNET_CONTAINER_multihashmap_remove (oidc_code_cache,
2567 &cache_key,
2568 cached_code));
2569
2570 // decode code
2571 if (GNUNET_OK != OIDC_parse_authz_code (&handle->ticket.audience,
2572 cached_code, NULL, &ticket,
2573 &cl, &pl, &nonce,
2574 OIDC_VERIFICATION_NO_CODE_VERIFIER))
2575 {
2576 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2577 handle->edesc = GNUNET_strdup ("invalid code");
2578 handle->response_code = MHD_HTTP_BAD_REQUEST;
2579 GNUNET_free (cached_code);
2580 if (NULL != nonce)
2581 GNUNET_free (nonce);
2582 GNUNET_SCHEDULER_add_now (&do_error, handle);
2583 return;
2584 }
2585
2586 GNUNET_free (cached_code);
2587
2588 result_str = OIDC_generate_userinfo (&handle->ticket.identity,
2589 cl,
2590 pl);
2591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str);
2592 resp = GNUNET_REST_create_response (result_str);
2593 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2594 GNUNET_free (result_str);
2595 GNUNET_free (nonce);
2596 GNUNET_RECLAIM_attribute_list_destroy (cl);
2597 GNUNET_RECLAIM_presentation_list_destroy (pl);
2598 cleanup_handle (handle);
2599}
2600
2601
2602/**
2603 * Responds to userinfo GET and url-encoded POST request
2604 *
2605 * @param con_handle the connection handle
2606 * @param url the url
2607 * @param cls the RequestHandle
2608 */
2609static void
2610userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2611 const char *url,
2612 void *cls)
2613{
2614 // TODO expiration time
2615 struct RequestHandle *handle = cls;
2616 struct GNUNET_RECLAIM_Ticket *ticket;
2617 char delimiter[] = " ";
2618 struct GNUNET_HashCode cache_key;
2619 char *authorization;
2620 char *authorization_type;
2621 char *authorization_access_token;
2622 const struct EgoEntry *aud_ego;
2623 const struct GNUNET_CRYPTO_PrivateKey *privkey;
2624
2625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting userinfo\n");
2626 GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2627 strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2628 &cache_key);
2629 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2630 ->header_param_map,
2631 &cache_key))
2632 {
2633 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2634 handle->edesc = GNUNET_strdup ("No Access Token");
2635 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2636 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2637 return;
2638 }
2639 authorization =
2640 GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2641 &cache_key);
2642
2643 // split header in "Bearer" and access_token
2644 authorization = GNUNET_strdup (authorization);
2645 authorization_type = strtok (authorization, delimiter);
2646 if ((NULL == authorization_type) ||
2647 (0 != strcmp ("Bearer", authorization_type)))
2648 {
2649 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2650 handle->edesc = GNUNET_strdup ("No Access Token");
2651 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2652 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2653 GNUNET_free (authorization);
2654 return;
2655 }
2656 authorization_access_token = strtok (NULL, delimiter);
2657 if (NULL == authorization_access_token)
2658 {
2659 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2660 handle->edesc = GNUNET_strdup ("Access token missing");
2661 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2662 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2663 GNUNET_free (authorization);
2664 return;
2665 }
2666
2667 if (GNUNET_OK != OIDC_access_token_parse (authorization_access_token,
2668 &ticket))
2669 {
2670 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2671 handle->edesc = GNUNET_strdup ("The access token is invalid");
2672 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2673 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2674 GNUNET_free (authorization);
2675 return;
2676
2677 }
2678 GNUNET_assert (NULL != ticket);
2679 handle->ticket = *ticket;
2680 GNUNET_free (ticket);
2681 aud_ego = find_ego (handle, &handle->ticket.audience);
2682 if (NULL == aud_ego)
2683 {
2684 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2685 handle->edesc = GNUNET_strdup ("The access token expired");
2686 handle->response_code = MHD_HTTP_UNAUTHORIZED;
2687 GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2688 GNUNET_free (authorization);
2689 return;
2690 }
2691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Consuming ticket\n");
2692 privkey = GNUNET_IDENTITY_ego_get_private_key (aud_ego->ego);
2693 handle->attr_userinfo_list =
2694 GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
2695 handle->presentations =
2696 GNUNET_new (struct GNUNET_RECLAIM_PresentationList);
2697
2698 /* If the consume takes too long, we use values from the cache */
2699 handle->access_token = GNUNET_strdup (authorization_access_token);
2700 handle->consume_timeout_op = GNUNET_SCHEDULER_add_delayed (consume_timeout,
2701 &consume_fail,
2702 handle);
2703 handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
2704 privkey,
2705 &handle->ticket,
2706 &consume_ticket,
2707 handle);
2708 GNUNET_free (authorization);
2709}
2710
2711/**
2712 * Responds to /jwks.json
2713 *
2714 * @param con_handle the connection handle
2715 * @param url the url
2716 * @param cls the RequestHandle
2717 */
2718static void
2719jwks_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2720 const char *url,
2721 void *cls)
2722{
2723 char *oidc_directory;
2724 char *oidc_jwk_path;
2725 char *oidc_jwk_pub_str;
2726 json_t *oidc_jwk;
2727 struct MHD_Response *resp;
2728 struct RequestHandle *handle = cls;
2729
2730 oidc_jwk_path = get_oidc_jwk_path (cls);
2731 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2732
2733 // Check if secret JWK exists
2734 if (! oidc_jwk)
2735 {
2736 // Generate and save a new key
2737 oidc_jwk = generate_jwk ();
2738 oidc_directory = get_oidc_dir_path (cls);
2739
2740 // Create new oidc directory
2741 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2742 {
2743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2744 ("Failed to create directory `%s' for storing oidc data\n"),
2745 oidc_directory);
2746 }
2747 else
2748 {
2749 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2750 }
2751 }
2752
2753 // Convert secret JWK to public JWK
2754 jose_jwk_pub (NULL, oidc_jwk);
2755
2756 // Encode JWK as string and return to API endpoint
2757 oidc_jwk_pub_str = json_dumps (oidc_jwk, JSON_INDENT (1));
2758 resp = GNUNET_REST_create_response (oidc_jwk_pub_str);
2759 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2760 json_decref (oidc_jwk);
2761 GNUNET_free (oidc_jwk_pub_str);
2762 GNUNET_free (oidc_jwk_pub_str);
2763 cleanup_handle (handle);
2764}
2765
2766/**
2767 * If listing is enabled, prints information about the egos.
2768 *
2769 * This function is initially called for all egos and then again
2770 * whenever a ego's identifier changes or if it is deleted. At the
2771 * end of the initial pass over all egos, the function is once called
2772 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2773 * be invoked in the future or that there was an error.
2774 *
2775 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
2776 * function is only called ONCE, and 'NULL' being passed in 'ego' does
2777 * indicate an error (for example because name is taken or no default value is
2778 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
2779 * value WILL be passed to a subsequent call to the identity callback of
2780 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
2781 *
2782 * When an identity is renamed, this function is called with the
2783 * (known) ego but the NEW identifier.
2784 *
2785 * When an identity is deleted, this function is called with the
2786 * (known) ego and "NULL" for the 'identifier'. In this case,
2787 * the 'ego' is henceforth invalid (and the 'ctx' should also be
2788 * cleaned up).
2789 *
2790 * @param cls closure
2791 * @param ego ego handle
2792 * @param ctx context for application to store data for this ego
2793 * (during the lifetime of this process, initially NULL)
2794 * @param identifier identifier assigned by the user for this ego,
2795 * NULL if the user just deleted the ego and it
2796 * must thus no longer be used
2797 */
2798static void
2799list_ego (void *cls,
2800 struct GNUNET_IDENTITY_Ego *ego,
2801 void **ctx,
2802 const char *identifier)
2803{
2804 struct EgoEntry *ego_entry;
2805 struct GNUNET_CRYPTO_PublicKey pk;
2806
2807 if (NULL == ego)
2808 {
2809 state = ID_REST_STATE_POST_INIT;
2810 return;
2811 }
2812 if (ID_REST_STATE_INIT == state)
2813
2814 {
2815 ego_entry = GNUNET_new (struct EgoEntry);
2816 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2817 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
2818 ego_entry->ego = ego;
2819 ego_entry->identifier = GNUNET_strdup (identifier);
2820 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2821 ego_tail,
2822 ego_entry);
2823 return;
2824 }
2825 /* Ego renamed or added */
2826 if (identifier != NULL)
2827 {
2828 for (ego_entry = ego_head; NULL != ego_entry;
2829 ego_entry = ego_entry->next)
2830 {
2831 if (ego_entry->ego == ego)
2832 {
2833 /* Rename */
2834 GNUNET_free (ego_entry->identifier);
2835 ego_entry->identifier = GNUNET_strdup (identifier);
2836 break;
2837 }
2838 }
2839 if (NULL == ego_entry)
2840 {
2841 /* Add */
2842 ego_entry = GNUNET_new (struct EgoEntry);
2843 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2844 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
2845 ego_entry->ego = ego;
2846 ego_entry->identifier = GNUNET_strdup (identifier);
2847 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
2848 ego_tail,
2849 ego_entry);
2850 }
2851 }
2852 else
2853 {
2854 /* Delete */
2855 for (ego_entry = ego_head; NULL != ego_entry;
2856 ego_entry = ego_entry->next)
2857 {
2858 if (ego_entry->ego == ego)
2859 break;
2860 }
2861 if (NULL == ego_entry)
2862 return; /* Not found */
2863
2864 GNUNET_CONTAINER_DLL_remove (ego_head,
2865 ego_tail,
2866 ego_entry);
2867 GNUNET_free (ego_entry->identifier);
2868 GNUNET_free (ego_entry->keystring);
2869 GNUNET_free (ego_entry);
2870 return;
2871 }
2872}
2873
2874
2875static void
2876oidc_config_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2877 const char *url,
2878 void *cls)
2879{
2880 json_t *oidc_config;
2881 json_t *auth_methods;
2882 json_t *sig_algs;
2883 json_t *scopes;
2884 json_t *response_types;
2885 json_t *sub_types;
2886 json_t *claim_types;
2887 char *oidc_config_str;
2888 struct MHD_Response *resp;
2889 struct RequestHandle *handle = cls;
2890
2891 oidc_config = json_object ();
2892 // FIXME get from config?
2893 json_object_set_new (oidc_config,
2894 "issuer", json_string ("http://localhost:7776"));
2895 json_object_set_new (oidc_config,
2896 "authorization_endpoint",
2897 json_string ("https://api.reclaim/openid/authorize"));
2898 json_object_set_new (oidc_config,
2899 "token_endpoint",
2900 json_string ("http://localhost:7776/openid/token"));
2901 auth_methods = json_array ();
2902 json_array_append_new (auth_methods,
2903 json_string ("client_secret_basic"));
2904 json_array_append_new (auth_methods,
2905 json_string ("client_secret_post"));
2906 json_object_set_new (oidc_config,
2907 "token_endpoint_auth_methods_supported",
2908 auth_methods);
2909 sig_algs = json_array ();
2910 json_array_append_new (sig_algs,
2911 json_string ("HS512"));
2912 json_array_append_new (sig_algs,
2913 json_string ("RS256"));
2914 json_object_set_new (oidc_config,
2915 "id_token_signing_alg_values_supported",
2916 sig_algs);
2917 json_object_set_new (oidc_config,
2918 "jwks_uri",
2919 json_string ("http://localhost:7776/jwks.json"));
2920 json_object_set_new (oidc_config,
2921 "userinfo_endpoint",
2922 json_string ("http://localhost:7776/openid/userinfo"));
2923 scopes = json_array ();
2924 json_array_append_new (scopes,
2925 json_string ("openid"));
2926 json_array_append_new (scopes,
2927 json_string ("profile"));
2928 json_array_append_new (scopes,
2929 json_string ("email"));
2930 json_array_append_new (scopes,
2931 json_string ("address"));
2932 json_array_append_new (scopes,
2933 json_string ("phone"));
2934 json_object_set_new (oidc_config,
2935 "scopes_supported",
2936 scopes);
2937 response_types = json_array ();
2938 json_array_append_new (response_types,
2939 json_string ("code"));
2940 json_object_set_new (oidc_config,
2941 "response_types_supported",
2942 response_types);
2943 sub_types = json_array ();
2944 json_array_append_new (sub_types,
2945 json_string ("public")); /* no pairwise support */
2946 json_object_set_new (oidc_config,
2947 "subject_types_supported",
2948 sub_types);
2949 claim_types = json_array ();
2950 json_array_append_new (claim_types,
2951 json_string ("normal"));
2952 json_array_append_new (claim_types,
2953 json_string ("aggregated"));
2954 json_object_set_new (oidc_config,
2955 "claim_types_supported",
2956 claim_types);
2957 json_object_set_new (oidc_config,
2958 "claims_parameter_supported",
2959 json_boolean (1));
2960 oidc_config_str = json_dumps (oidc_config, JSON_INDENT (1));
2961 resp = GNUNET_REST_create_response (oidc_config_str);
2962 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2963 json_decref (oidc_config);
2964 GNUNET_free (oidc_config_str);
2965 cleanup_handle (handle);
2966}
2967
2968
2969/**
2970 * Respond to OPTIONS request
2971 *
2972 * @param con_handle the connection handle
2973 * @param url the url
2974 * @param cls the RequestHandle
2975 */
2976static void
2977oidc_config_cors (struct GNUNET_REST_RequestHandle *con_handle,
2978 const char *url,
2979 void *cls)
2980{
2981 struct MHD_Response *resp;
2982 struct RequestHandle *handle = cls;
2983
2984 // For now, independent of path return all options
2985 resp = GNUNET_REST_create_response (NULL);
2986 GNUNET_assert (MHD_NO !=
2987 MHD_add_response_header (resp,
2988 "Access-Control-Allow-Methods",
2989 allow_methods));
2990 GNUNET_assert (MHD_NO !=
2991 MHD_add_response_header (resp,
2992 "Access-Control-Allow-Origin",
2993 "*"));
2994 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2995 cleanup_handle (handle);
2996 return;
2997}
2998
2999
3000static enum GNUNET_GenericReturnValue
3001rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
3002 GNUNET_REST_ResultProcessor proc,
3003 void *proc_cls)
3004{
3005 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
3006 struct GNUNET_REST_RequestHandlerError err;
3007 static const struct GNUNET_REST_RequestHandler handlers[] =
3008 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
3009 { MHD_HTTP_METHOD_POST,
3010 GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint }, // url-encoded
3011 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
3012 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
3013 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
3014 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
3015 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_JWKS, &jwks_endpoint },
3016 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG,
3017 &oidc_config_endpoint },
3018 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG,
3019 &oidc_config_cors },
3020 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
3021 GNUNET_REST_HANDLER_END };
3022
3023 handle->oidc = GNUNET_new (struct OIDC_Variables);
3024 if (NULL == OIDC_cookie_jar_map)
3025 OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10,
3026 GNUNET_NO);
3027 if (NULL == oidc_code_cache)
3028 oidc_code_cache = GNUNET_CONTAINER_multihashmap_create (10,
3029 GNUNET_NO);
3030
3031 handle->response_code = 0;
3032 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
3033 handle->proc_cls = proc_cls;
3034 handle->proc = proc;
3035 handle->rest_handle = rest_handle;
3036 handle->url = GNUNET_strdup (rest_handle->url);
3037 handle->timeout_task =
3038 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
3039 GNUNET_CONTAINER_DLL_insert (requests_head,
3040 requests_tail,
3041 handle);
3042 if (handle->url[strlen (handle->url) - 1] == '/')
3043 handle->url[strlen (handle->url) - 1] = '\0';
3044 if (GNUNET_NO ==
3045 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
3046 return GNUNET_NO;
3047
3048 return GNUNET_YES;
3049}
3050
3051
3052/**
3053 * Entry point for the plugin.
3054 *
3055 * @param cls Config info
3056 * @return NULL on error, otherwise the plugin context
3057 */
3058void *
3059libgnunet_plugin_rest_openid_connect_init (void *cls)
3060{
3061 static struct Plugin plugin;
3062 struct GNUNET_REST_Plugin *api;
3063
3064 cfg = cls;
3065 if (NULL != plugin.cfg)
3066 return NULL; /* can only initialize once! */
3067 memset (&plugin, 0, sizeof(struct Plugin));
3068 plugin.cfg = cfg;
3069 api = GNUNET_new (struct GNUNET_REST_Plugin);
3070 api->cls = &plugin;
3071 api->name = GNUNET_REST_API_NS_OIDC;
3072 api->process_request = &rest_identity_process_request;
3073 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
3074 gns_handle = GNUNET_GNS_connect (cfg);
3075 idp = GNUNET_RECLAIM_connect (cfg);
3076 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
3077 "reclaim-rest-plugin",
3078 "OIDC_USERINFO_CONSUME_TIMEOUT",
3079 &consume_timeout))
3080 {
3081 consume_timeout = CONSUME_TIMEOUT;
3082 }
3083
3084
3085 state = ID_REST_STATE_INIT;
3086 GNUNET_asprintf (&allow_methods,
3087 "%s, %s, %s, %s, %s",
3088 MHD_HTTP_METHOD_GET,
3089 MHD_HTTP_METHOD_POST,
3090 MHD_HTTP_METHOD_PUT,
3091 MHD_HTTP_METHOD_DELETE,
3092 MHD_HTTP_METHOD_OPTIONS);
3093
3094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3095 _ ("OpenID Connect REST API initialized\n"));
3096 return api;
3097}
3098
3099
3100static int
3101cleanup_hashmap (void *cls, const struct GNUNET_HashCode *key, void *value)
3102{
3103 GNUNET_free (value);
3104 return GNUNET_YES;
3105}
3106
3107
3108/**
3109 * Exit point from the plugin.
3110 *
3111 * @param cls the plugin context (as returned by "init")
3112 * @return always NULL
3113 */
3114void *
3115libgnunet_plugin_rest_openid_connect_done (void *cls)
3116{
3117 struct GNUNET_REST_Plugin *api = cls;
3118 struct Plugin *plugin = api->cls;
3119 struct EgoEntry *ego_entry;
3120
3121 plugin->cfg = NULL;
3122 while (NULL != requests_head)
3123 cleanup_handle (requests_head);
3124 if (NULL != OIDC_cookie_jar_map)
3125 {
3126 GNUNET_CONTAINER_multihashmap_iterate (OIDC_cookie_jar_map,
3127 &cleanup_hashmap,
3128 NULL);
3129 GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
3130 }
3131 if (NULL != oidc_code_cache)
3132 {
3133 GNUNET_CONTAINER_multihashmap_iterate (oidc_code_cache,
3134 &cleanup_hashmap,
3135 NULL);
3136 GNUNET_CONTAINER_multihashmap_destroy (oidc_code_cache);
3137 }
3138
3139 GNUNET_free (allow_methods);
3140 if (NULL != gns_handle)
3141 GNUNET_GNS_disconnect (gns_handle);
3142 if (NULL != identity_handle)
3143 GNUNET_IDENTITY_disconnect (identity_handle);
3144 if (NULL != idp)
3145 GNUNET_RECLAIM_disconnect (idp);
3146 while (NULL != (ego_entry = ego_head))
3147 {
3148 GNUNET_CONTAINER_DLL_remove (ego_head,
3149 ego_tail,
3150 ego_entry);
3151 GNUNET_free (ego_entry->identifier);
3152 GNUNET_free (ego_entry->keystring);
3153 GNUNET_free (ego_entry);
3154 }
3155 GNUNET_free (api);
3156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3157 "OpenID Connect REST plugin is finished\n");
3158 return NULL;
3159}
3160
3161
3162/* end of plugin_rest_openid_connect.c */
diff --git a/src/rest-plugin/reclaim/plugin_rest_pabc.c b/src/rest-plugin/reclaim/plugin_rest_pabc.c
deleted file mode 100644
index 4b7d21df3..000000000
--- a/src/rest-plugin/reclaim/plugin_rest_pabc.c
+++ /dev/null
@@ -1,667 +0,0 @@
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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @file reclaim/plugin_rest_pabc.c
23 * @brief GNUnet pabc REST plugin
24 *
25 */
26#include "platform.h"
27#include "microhttpd.h"
28#include <inttypes.h>
29#include <jansson.h>
30#include <pabc/pabc.h>
31#include "gnunet_reclaim_lib.h"
32#include "gnunet_reclaim_service.h"
33#include "gnunet_rest_lib.h"
34#include "gnunet_rest_plugin.h"
35#include "gnunet_signatures.h"
36#include "pabc_helper.h"
37
38/**
39 * REST root namespace
40 */
41#define GNUNET_REST_API_NS_PABC "/pabc"
42
43/**
44 * Credential request endpoint
45 */
46#define GNUNET_REST_API_NS_PABC_CR "/pabc/cr"
47
48/**
49 * The configuration handle
50 */
51const struct GNUNET_CONFIGURATION_Handle *cfg;
52
53/**
54 * HTTP methods allows for this plugin
55 */
56static char *allow_methods;
57
58/**
59 * @brief struct returned by the initialization function of the plugin
60 */
61struct Plugin
62{
63 const struct GNUNET_CONFIGURATION_Handle *cfg;
64};
65
66
67struct RequestHandle
68{
69 /**
70 * DLL
71 */
72 struct RequestHandle *next;
73
74 /**
75 * DLL
76 */
77 struct RequestHandle *prev;
78
79 /**
80 * Rest connection
81 */
82 struct GNUNET_REST_RequestHandle *rest_handle;
83
84 /**
85 * Desired timeout for the lookup (default is no timeout).
86 */
87 struct GNUNET_TIME_Relative timeout;
88
89 /**
90 * ID of a task associated with the resolution process.
91 */
92 struct GNUNET_SCHEDULER_Task *timeout_task;
93
94 /**
95 * The plugin result processor
96 */
97 GNUNET_REST_ResultProcessor proc;
98
99 /**
100 * The closure of the result processor
101 */
102 void *proc_cls;
103
104 /**
105 * The url
106 */
107 char *url;
108
109 /**
110 * Error response message
111 */
112 char *emsg;
113
114 /**
115 * Response code
116 */
117 int response_code;
118
119 /**
120 * Response object
121 */
122 json_t *resp_object;
123};
124
125/**
126 * DLL
127 */
128static struct RequestHandle *requests_head;
129
130/**
131 * DLL
132 */
133static struct RequestHandle *requests_tail;
134
135
136/**
137 * Cleanup lookup handle
138 * @param handle Handle to clean up
139 */
140static void
141cleanup_handle (void *cls)
142{
143 struct RequestHandle *handle = cls;
144
145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
146 if (NULL != handle->resp_object)
147 json_decref (handle->resp_object);
148 if (NULL != handle->timeout_task)
149 GNUNET_SCHEDULER_cancel (handle->timeout_task);
150 if (NULL != handle->url)
151 GNUNET_free (handle->url);
152 if (NULL != handle->emsg)
153 GNUNET_free (handle->emsg);
154 GNUNET_CONTAINER_DLL_remove (requests_head,
155 requests_tail,
156 handle);
157 GNUNET_free (handle);
158}
159
160
161/**
162 * Task run on error, sends error message. Cleans up everything.
163 *
164 * @param cls the `struct RequestHandle`
165 */
166static void
167do_error (void *cls)
168{
169 struct RequestHandle *handle = cls;
170 struct MHD_Response *resp;
171 char *json_error;
172
173 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", handle->emsg);
174 if (0 == handle->response_code)
175 {
176 handle->response_code = MHD_HTTP_BAD_REQUEST;
177 }
178 resp = GNUNET_REST_create_response (json_error);
179 MHD_add_response_header (resp, "Content-Type", "application/json");
180 handle->proc (handle->proc_cls, resp, handle->response_code);
181 cleanup_handle (handle);
182 GNUNET_free (json_error);
183}
184
185
186/**
187 * Task run on timeout, sends error message. Cleans up everything.
188 *
189 * @param cls the `struct RequestHandle`
190 */
191static void
192do_timeout (void *cls)
193{
194 struct RequestHandle *handle = cls;
195
196 handle->timeout_task = NULL;
197 do_error (handle);
198}
199
200
201static void
202return_response (void *cls)
203{
204 char *result_str;
205 struct RequestHandle *handle = cls;
206 struct MHD_Response *resp;
207
208 result_str = json_dumps (handle->resp_object, 0);
209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
210 resp = GNUNET_REST_create_response (result_str);
211 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
212 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
213 GNUNET_free (result_str);
214 cleanup_handle (handle);
215}
216
217
218static enum pabc_status
219set_attributes_from_idtoken (const struct pabc_context *ctx,
220 const struct pabc_public_parameters *pp,
221 struct pabc_user_context *usr_ctx,
222 const char *id_token)
223{
224 json_t *payload_json;
225 json_t *value;
226 json_error_t json_err;
227 const char *key;
228 const char *jwt_body;
229 char *decoded_jwt;
230 char delim[] = ".";
231 char *jwt_string;
232 const char *pabc_key;
233 enum pabc_status status;
234
235 // FIXME parse JWT
236 jwt_string = GNUNET_strndup (id_token, strlen (id_token));
237 jwt_body = strtok (jwt_string, delim);
238 jwt_body = strtok (NULL, delim);
239 GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body),
240 (void **) &decoded_jwt);
241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decoded ID Token: %s\n", decoded_jwt);
242 payload_json = json_loads (decoded_jwt, JSON_DECODE_ANY, &json_err);
243 GNUNET_free (decoded_jwt);
244
245 json_object_foreach (payload_json, key, value)
246 {
247 pabc_key = key;
248 if (0 == strcmp ("iss", key))
249 pabc_key = "issuer"; // rename
250 if (0 == strcmp ("sub", key))
251 pabc_key = "subject"; // rename
252 if (0 == strcmp ("jti", key))
253 continue;
254 if (0 == strcmp ("exp", key))
255 pabc_key = "expiration"; // rename
256 if (0 == strcmp ("iat", key))
257 continue;
258 if (0 == strcmp ("nbf", key))
259 continue;
260 if (0 == strcmp ("aud", key))
261 continue;
262 char *tmp_val;
263 if (json_is_string (value))
264 tmp_val = GNUNET_strdup (json_string_value (value));
265 else
266 tmp_val = json_dumps (value, JSON_ENCODE_ANY);
267 if (NULL == tmp_val)
268 {
269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270 "Unable to encode JSON value for `%s'\n", key);
271 continue;
272 }
273 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274 "Setting `%s' to `%s'\n", key, tmp_val);
275 status = pabc_set_attribute_value_by_name (ctx, pp, usr_ctx,
276 pabc_key,
277 tmp_val);
278 GNUNET_free (tmp_val);
279 if (PABC_OK != status)
280 {
281 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
282 "Failed to set attribute `%s'.\n", key);
283 }
284 }
285 GNUNET_free (jwt_string);
286 return PABC_OK;
287}
288
289
290static enum GNUNET_GenericReturnValue
291setup_new_user_context (struct pabc_context *ctx,
292 struct pabc_public_parameters *pp,
293 struct pabc_user_context **usr_ctx)
294{
295 if (PABC_OK != pabc_new_user_context (ctx, pp, usr_ctx))
296 return GNUNET_SYSERR;
297
298 if (PABC_OK != pabc_populate_user_context (ctx, *usr_ctx))
299 {
300 pabc_free_user_context (ctx, pp, usr_ctx);
301 return GNUNET_SYSERR;
302 }
303 return GNUNET_OK;
304}
305
306
307static void
308cr_cont (struct GNUNET_REST_RequestHandle *con_handle,
309 const char *url,
310 void *cls)
311{
312 struct RequestHandle *handle = cls;
313 char term_data[handle->rest_handle->data_size + 1];
314 char *response_str;
315 json_t *data_json;
316 json_t *nonce_json;
317 json_t *pp_json;
318 json_t *idtoken_json;
319 json_t *iss_json;
320 json_t *identity_json;
321 json_error_t err;
322 struct pabc_public_parameters *pp = NULL;
323 struct pabc_context *ctx = NULL;
324 struct pabc_user_context *usr_ctx = NULL;
325 struct pabc_credential_request *cr = NULL;
326 struct pabc_nonce *nonce = NULL;
327 enum pabc_status status;
328
329
330 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
331 "Credential request...\n");
332
333 if (0 >= handle->rest_handle->data_size)
334 {
335 GNUNET_SCHEDULER_add_now (&do_error, handle);
336 return;
337 }
338
339 term_data[handle->rest_handle->data_size] = '\0';
340 GNUNET_memcpy (term_data,
341 handle->rest_handle->data,
342 handle->rest_handle->data_size);
343 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
344 if (NULL == data_json)
345 {
346 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
347 "Unable to parse %s\n", term_data);
348 GNUNET_SCHEDULER_add_now (&do_error, handle);
349 return;
350 }
351 if (! json_is_object (data_json))
352 {
353 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
354 "Unable to parse %s\n", term_data);
355 json_decref (data_json);
356 GNUNET_SCHEDULER_add_now (&do_error, handle);
357 return;
358 }
359
360 nonce_json = json_object_get (data_json, "nonce");
361 if (NULL == nonce_json)
362 {
363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364 "Unable to parse nonce\n");
365 json_decref (data_json);
366 GNUNET_SCHEDULER_add_now (&do_error, handle);
367 return;
368 }
369 iss_json = json_object_get (data_json, "issuer");
370 if (NULL == iss_json)
371 {
372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
373 "Unable to parse issuer\n");
374 json_decref (data_json);
375 GNUNET_SCHEDULER_add_now (&do_error, handle);
376 return;
377 }
378 identity_json = json_object_get (data_json, "identity");
379 if (NULL == identity_json)
380 {
381 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382 "Unable to parse identity\n");
383 json_decref (data_json);
384 GNUNET_SCHEDULER_add_now (&do_error, handle);
385 return;
386 }
387 idtoken_json = json_object_get (data_json, "id_token");
388 if (NULL == idtoken_json)
389 {
390 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
391 "Unable to parse id_token\n");
392 json_decref (data_json);
393 GNUNET_SCHEDULER_add_now (&do_error, handle);
394 return;
395 }
396 pp_json = json_object_get (data_json, "public_params");
397 if (NULL == pp_json)
398 {
399 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
400 "Unable to parse public parameters\n");
401 json_decref (data_json);
402 GNUNET_SCHEDULER_add_now (&do_error, handle);
403 return;
404 }
405
406 PABC_ASSERT (pabc_new_ctx (&ctx));
407 char *pp_str = json_dumps (pp_json, JSON_ENCODE_ANY);
408 status = pabc_decode_and_new_public_parameters (ctx,
409 &pp,
410 pp_str);
411 char *ppid;
412 GNUNET_assert (PABC_OK == pabc_cred_get_ppid_from_pp (pp_str, &ppid));
413 GNUNET_free (pp_str);
414 if (status != PABC_OK)
415 {
416 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
417 "Failed to read public parameters: %s\n",
418 pp_str);
419 json_decref (data_json);
420 GNUNET_SCHEDULER_add_now (&do_error, handle);
421 return;
422 }
423 // (Over)write parameters
424 status = PABC_write_public_parameters (json_string_value (iss_json),
425 pp);
426 if (status != PABC_OK)
427 {
428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 "Failed to write public parameters.\n");
430 json_decref (data_json);
431 GNUNET_SCHEDULER_add_now (&do_error, handle);
432 return;
433 }
434 status = PABC_read_usr_ctx (json_string_value (identity_json),
435 json_string_value (iss_json),
436 ctx, pp, &usr_ctx);
437 if (PABC_OK != status)
438 {
439 if (GNUNET_OK != setup_new_user_context (ctx, pp, &usr_ctx))
440 {
441 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to setup user context.\n");
442 pabc_free_public_parameters (ctx, &pp);
443 json_decref (data_json);
444 GNUNET_SCHEDULER_add_now (&do_error, handle);
445 return;
446 }
447 PABC_write_usr_ctx (json_string_value (identity_json),
448 json_string_value (iss_json),
449 ctx, pp, usr_ctx);
450 }
451
452 // Set attributes from JWT to context
453 status = set_attributes_from_idtoken (ctx,
454 pp,
455 usr_ctx,
456 json_string_value (idtoken_json));
457 if (status != PABC_OK)
458 {
459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to set attributes.\n");
460 pabc_free_user_context (ctx, pp, &usr_ctx);
461 pabc_free_public_parameters (ctx, &pp);
462 json_decref (data_json);
463 GNUNET_SCHEDULER_add_now (&do_error, handle);
464 return;
465 }
466
467
468 // nonce
469 status = pabc_new_nonce (ctx, &nonce);
470 if (status != PABC_OK)
471 {
472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate nonce.\n");
473 pabc_free_user_context (ctx, pp, &usr_ctx);
474 pabc_free_public_parameters (ctx, &pp);
475 json_decref (data_json);
476 GNUNET_SCHEDULER_add_now (&do_error, handle);
477 return;
478 }
479 char *nonce_str = json_dumps (nonce_json, JSON_ENCODE_ANY);
480 status = pabc_decode_nonce (ctx, nonce, nonce_str);
481 if (status != PABC_OK)
482 {
483 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to decode nonce.\n");
484 pabc_free_nonce (ctx, &nonce);
485 pabc_free_user_context (ctx, pp, &usr_ctx);
486 pabc_free_public_parameters (ctx, &pp);
487 json_decref (data_json);
488 GNUNET_SCHEDULER_add_now (&do_error, handle);
489 return;
490 }
491
492 // cr
493 status = pabc_new_credential_request (ctx, pp, &cr);
494 if (PABC_OK != status)
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate cr.\n");
497 pabc_free_nonce (ctx, &nonce);
498 pabc_free_user_context (ctx, pp, &usr_ctx);
499 pabc_free_public_parameters (ctx, &pp);
500 json_decref (data_json);
501 GNUNET_SCHEDULER_add_now (&do_error, handle);
502 return;
503 }
504
505 status = pabc_gen_credential_request (ctx, pp, usr_ctx, nonce, cr);
506 if (PABC_OK != status)
507 {
508 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to generate cr.\n");
509 pabc_free_nonce (ctx, &nonce);
510 pabc_free_credential_request (ctx, pp, &cr);
511 pabc_free_user_context (ctx, pp, &usr_ctx);
512 pabc_free_public_parameters (ctx, &pp);
513 json_decref (data_json);
514 GNUNET_SCHEDULER_add_now (&do_error, handle);
515 return;
516 }
517 handle->resp_object = json_object ();
518 GNUNET_assert (PABC_OK == pabc_cred_encode_cr (ctx, pp, cr,
519 json_string_value (
520 identity_json),
521 ppid, &response_str));
522 if (PABC_OK != status)
523 {
524 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to serialize cr.\n");
525 pabc_free_nonce (ctx, &nonce);
526 pabc_free_credential_request (ctx, pp, &cr);
527 pabc_free_user_context (ctx, pp, &usr_ctx);
528 pabc_free_public_parameters (ctx, &pp);
529 json_decref (data_json);
530 GNUNET_SCHEDULER_add_now (&do_error, handle);
531 return;
532 }
533 json_decref (handle->resp_object);
534 handle->resp_object = json_loads (response_str, JSON_DECODE_ANY, &err);
535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", response_str);
536 GNUNET_free (response_str);
537
538 // clean up
539 pabc_free_nonce (ctx, &nonce);
540 pabc_free_credential_request (ctx, pp, &cr);
541 pabc_free_user_context (ctx, pp, &usr_ctx);
542 pabc_free_public_parameters (ctx, &pp);
543 GNUNET_SCHEDULER_add_now (&return_response, handle);
544 json_decref (data_json);
545}
546
547
548/**
549 * Respond to OPTIONS request
550 *
551 * @param con_handle the connection handle
552 * @param url the url
553 * @param cls the RequestHandle
554 */
555static void
556options_cont (struct GNUNET_REST_RequestHandle *con_handle,
557 const char *url,
558 void *cls)
559{
560 struct MHD_Response *resp;
561 struct RequestHandle *handle = cls;
562
563 // For now, independent of path return all options
564 resp = GNUNET_REST_create_response (NULL);
565 MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
566 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
567 cleanup_handle (handle);
568 return;
569}
570
571
572static enum GNUNET_GenericReturnValue
573rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
574 GNUNET_REST_ResultProcessor proc,
575 void *proc_cls)
576{
577 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
578 struct GNUNET_REST_RequestHandlerError err;
579 static const struct GNUNET_REST_RequestHandler handlers[] = {
580 {MHD_HTTP_METHOD_POST,
581 GNUNET_REST_API_NS_PABC_CR, &cr_cont },
582 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PABC, &options_cont },
583 GNUNET_REST_HANDLER_END
584 };
585
586 handle->response_code = 0;
587 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
588 handle->proc_cls = proc_cls;
589 handle->proc = proc;
590 handle->rest_handle = rest_handle;
591
592 handle->url = GNUNET_strdup (rest_handle->url);
593 if (handle->url[strlen (handle->url) - 1] == '/')
594 handle->url[strlen (handle->url) - 1] = '\0';
595 handle->timeout_task =
596 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
597 GNUNET_CONTAINER_DLL_insert (requests_head,
598 requests_tail,
599 handle);
600 if (GNUNET_NO ==
601 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
602 {
603 cleanup_handle (handle);
604 return GNUNET_NO;
605 }
606
607 return GNUNET_YES;
608}
609
610
611/**
612 * Entry point for the plugin.
613 *
614 * @param cls Config info
615 * @return NULL on error, otherwise the plugin context
616 */
617void *
618libgnunet_plugin_rest_pabc_init (void *cls)
619{
620 static struct Plugin plugin;
621 struct GNUNET_REST_Plugin *api;
622
623 cfg = cls;
624 if (NULL != plugin.cfg)
625 return NULL; /* can only initialize once! */
626 memset (&plugin, 0, sizeof(struct Plugin));
627 plugin.cfg = cfg;
628 api = GNUNET_new (struct GNUNET_REST_Plugin);
629 api->cls = &plugin;
630 api->name = GNUNET_REST_API_NS_PABC;
631 api->process_request = &rest_identity_process_request;
632 GNUNET_asprintf (&allow_methods,
633 "%s, %s",
634 MHD_HTTP_METHOD_POST,
635 MHD_HTTP_METHOD_OPTIONS);
636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
637 _ ("Identity Provider REST API initialized\n"));
638 return api;
639}
640
641
642/**
643 * Exit point from the plugin.
644 *
645 * @param cls the plugin context (as returned by "init")
646 * @return always NULL
647 */
648void *
649libgnunet_plugin_rest_reclaim_done (void *cls)
650{
651 struct GNUNET_REST_Plugin *api = cls;
652 struct Plugin *plugin = api->cls;
653 struct RequestHandle *request;
654
655 plugin->cfg = NULL;
656 while (NULL != (request = requests_head))
657 do_error (request);
658
659 GNUNET_free (allow_methods);
660 GNUNET_free (api);
661 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
662 "PABC REST plugin is finished\n");
663 return NULL;
664}
665
666
667/* end of plugin_rest_reclaim.c */
diff --git a/src/rest-plugin/reclaim/plugin_rest_reclaim.c b/src/rest-plugin/reclaim/plugin_rest_reclaim.c
deleted file mode 100644
index b2586109a..000000000
--- a/src/rest-plugin/reclaim/plugin_rest_reclaim.c
+++ /dev/null
@@ -1,1564 +0,0 @@
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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file reclaim/plugin_rest_reclaim.c
24 * @brief GNUnet reclaim REST plugin
25 *
26 */
27#include "platform.h"
28#include "microhttpd.h"
29#include <inttypes.h>
30#include <jansson.h>
31#include "gnunet_gns_service.h"
32#include "gnunet_gnsrecord_lib.h"
33#include "gnunet_identity_service.h"
34#include "gnunet_reclaim_lib.h"
35#include "gnunet_reclaim_service.h"
36#include "gnunet_rest_lib.h"
37#include "gnunet_rest_plugin.h"
38#include "gnunet_signatures.h"
39#include "json_reclaim.h"
40/**
41 * REST root namespace
42 */
43#define GNUNET_REST_API_NS_RECLAIM "/reclaim"
44
45/**
46 * Attribute namespace
47 */
48#define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes"
49
50/**
51 * Credential namespace
52 */
53#define GNUNET_REST_API_NS_RECLAIM_CREDENTIAL "/reclaim/credential"
54
55/**
56 * Ticket namespace
57 */
58#define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets"
59
60/**
61 * Revoke namespace
62 */
63#define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke"
64
65/**
66 * Revoke namespace
67 */
68#define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume"
69
70/**
71 * State while collecting all egos
72 */
73#define ID_REST_STATE_INIT 0
74
75/**
76 * Done collecting egos
77 */
78#define ID_REST_STATE_POST_INIT 1
79
80/**
81 * The configuration handle
82 */
83const struct GNUNET_CONFIGURATION_Handle *cfg;
84
85/**
86 * HTTP methods allows for this plugin
87 */
88static char *allow_methods;
89
90/**
91 * Ego list
92 */
93static struct EgoEntry *ego_head;
94
95/**
96 * Ego list
97 */
98static struct EgoEntry *ego_tail;
99
100/**
101 * The processing state
102 */
103static int state;
104
105/**
106 * Handle to Identity service.
107 */
108static struct GNUNET_IDENTITY_Handle *identity_handle;
109
110/**
111 * Identity Provider
112 */
113static struct GNUNET_RECLAIM_Handle *idp;
114
115/**
116 * @brief struct returned by the initialization function of the plugin
117 */
118struct Plugin
119{
120 const struct GNUNET_CONFIGURATION_Handle *cfg;
121};
122
123/**
124 * The ego list
125 */
126struct EgoEntry
127{
128 /**
129 * DLL
130 */
131 struct EgoEntry *next;
132
133 /**
134 * DLL
135 */
136 struct EgoEntry *prev;
137
138 /**
139 * Ego Identifier
140 */
141 char *identifier;
142
143 /**
144 * Public key string
145 */
146 char *keystring;
147
148 /**
149 * The Ego
150 */
151 struct GNUNET_IDENTITY_Ego *ego;
152};
153
154
155struct RequestHandle
156{
157 /**
158 * DLL
159 */
160 struct RequestHandle *next;
161
162 /**
163 * DLL
164 */
165 struct RequestHandle *prev;
166
167 /**
168 * Selected ego
169 */
170 struct EgoEntry *ego_entry;
171
172 /**
173 * Pointer to ego private key
174 */
175 struct GNUNET_CRYPTO_PrivateKey priv_key;
176
177 /**
178 * Rest connection
179 */
180 struct GNUNET_REST_RequestHandle *rest_handle;
181
182 /**
183 * Attribute claim list
184 */
185 struct GNUNET_RECLAIM_AttributeList *attr_list;
186
187 /**
188 * IDENTITY Operation
189 */
190 struct GNUNET_IDENTITY_Operation *op;
191
192 /**
193 * Idp Operation
194 */
195 struct GNUNET_RECLAIM_Operation *idp_op;
196
197 /**
198 * Attribute iterator
199 */
200 struct GNUNET_RECLAIM_AttributeIterator *attr_it;
201
202 /**
203 * Attribute iterator
204 */
205 struct GNUNET_RECLAIM_CredentialIterator *cred_it;
206
207 /**
208 * Ticket iterator
209 */
210 struct GNUNET_RECLAIM_TicketIterator *ticket_it;
211
212 /**
213 * A ticket
214 */
215 struct GNUNET_RECLAIM_Ticket ticket;
216
217 /**
218 * Desired timeout for the lookup (default is no timeout).
219 */
220 struct GNUNET_TIME_Relative timeout;
221
222 /**
223 * ID of a task associated with the resolution process.
224 */
225 struct GNUNET_SCHEDULER_Task *timeout_task;
226
227 /**
228 * The plugin result processor
229 */
230 GNUNET_REST_ResultProcessor proc;
231
232 /**
233 * The closure of the result processor
234 */
235 void *proc_cls;
236
237 /**
238 * The url
239 */
240 char *url;
241
242 /**
243 * Error response message
244 */
245 char *emsg;
246
247 /**
248 * Response code
249 */
250 int response_code;
251
252 /**
253 * Response object
254 */
255 json_t *resp_object;
256};
257
258/**
259 * DLL
260 */
261static struct RequestHandle *requests_head;
262
263/**
264 * DLL
265 */
266static struct RequestHandle *requests_tail;
267
268
269/**
270 * Cleanup lookup handle
271 * @param handle Handle to clean up
272 */
273static void
274cleanup_handle (void *cls)
275{
276 struct RequestHandle *handle = cls;
277
278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
279 if (NULL != handle->resp_object)
280 json_decref (handle->resp_object);
281 if (NULL != handle->timeout_task)
282 GNUNET_SCHEDULER_cancel (handle->timeout_task);
283 if (NULL != handle->attr_it)
284 GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
285 if (NULL != handle->cred_it)
286 GNUNET_RECLAIM_get_credentials_stop (handle->cred_it);
287 if (NULL != handle->ticket_it)
288 GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
289 if (NULL != handle->url)
290 GNUNET_free (handle->url);
291 if (NULL != handle->emsg)
292 GNUNET_free (handle->emsg);
293 if (NULL != handle->attr_list)
294 GNUNET_RECLAIM_attribute_list_destroy (handle->attr_list);
295 GNUNET_CONTAINER_DLL_remove (requests_head,
296 requests_tail,
297 handle);
298 GNUNET_free (handle);
299}
300
301
302/**
303 * Task run on error, sends error message. Cleans up everything.
304 *
305 * @param cls the `struct RequestHandle`
306 */
307static void
308do_error (void *cls)
309{
310 struct RequestHandle *handle = cls;
311 struct MHD_Response *resp;
312 char *json_error;
313
314 GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", handle->emsg);
315 if (0 == handle->response_code)
316 {
317 handle->response_code = MHD_HTTP_BAD_REQUEST;
318 }
319 resp = GNUNET_REST_create_response (json_error);
320 GNUNET_assert (MHD_NO != MHD_add_response_header (resp, "Content-Type", "application/json"));
321 handle->proc (handle->proc_cls, resp, handle->response_code);
322 cleanup_handle (handle);
323 GNUNET_free (json_error);
324}
325
326
327/**
328 * Task run on timeout, sends error message. Cleans up everything.
329 *
330 * @param cls the `struct RequestHandle`
331 */
332static void
333do_timeout (void *cls)
334{
335 struct RequestHandle *handle = cls;
336
337 handle->timeout_task = NULL;
338 do_error (handle);
339}
340
341
342static void
343collect_error_cb (void *cls)
344{
345 GNUNET_SCHEDULER_add_now (&do_error, cls);
346}
347
348
349static void
350finished_cont (void *cls, int32_t success, const char *emsg)
351{
352 struct RequestHandle *handle = cls;
353 struct MHD_Response *resp;
354
355 handle->idp_op = NULL;
356 if (GNUNET_OK != success)
357 {
358 GNUNET_SCHEDULER_add_now (&do_error, handle);
359 return;
360 }
361 resp = GNUNET_REST_create_response (emsg);
362 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
363 "Content-Type",
364 "application/json"));
365 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
366 "Access-Control-Allow-Methods",
367 allow_methods));
368 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
369 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
370}
371
372
373static void
374delete_finished_cb (void *cls, int32_t success, const char *emsg)
375{
376 struct RequestHandle *handle = cls;
377 struct MHD_Response *resp;
378
379 if (GNUNET_OK != success)
380 {
381 GNUNET_SCHEDULER_add_now (&do_error, handle);
382 return;
383 }
384 resp = GNUNET_REST_create_response (emsg);
385 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
386 "Access-Control-Allow-Methods",
387 allow_methods));
388 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
389 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
390}
391
392
393/**
394 * Return attributes for identity
395 *
396 * @param cls the request handle
397 */
398static void
399return_response (void *cls)
400{
401 char *result_str;
402 struct RequestHandle *handle = cls;
403 struct MHD_Response *resp;
404
405 result_str = json_dumps (handle->resp_object, 0);
406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
407 resp = GNUNET_REST_create_response (result_str);
408 GNUNET_assert (MHD_NO !=
409 MHD_add_response_header (resp,
410 "Access-Control-Allow-Methods",
411 allow_methods));
412 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
413 GNUNET_free (result_str);
414 cleanup_handle (handle);
415}
416
417
418static void
419collect_finished_cb (void *cls)
420{
421 struct RequestHandle *handle = cls;
422
423 // Done
424 handle->attr_it = NULL;
425 handle->cred_it = NULL;
426 handle->ticket_it = NULL;
427 GNUNET_SCHEDULER_add_now (&return_response, handle);
428}
429
430
431/**
432 * Collect all attributes for an ego
433 *
434 */
435static void
436ticket_collect (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
437{
438 json_t *json_resource;
439 struct RequestHandle *handle = cls;
440 json_t *value;
441 char *tmp;
442
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
444 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(ticket->rnd));
445 json_resource = json_object ();
446 GNUNET_free (tmp);
447 json_array_append (handle->resp_object, json_resource);
448
449 tmp =
450 GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
451 sizeof(struct
452 GNUNET_CRYPTO_PublicKey));
453 value = json_string (tmp);
454 json_object_set_new (json_resource, "issuer", value);
455 GNUNET_free (tmp);
456 tmp =
457 GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
458 sizeof(struct
459 GNUNET_CRYPTO_PublicKey));
460 value = json_string (tmp);
461 json_object_set_new (json_resource, "audience", value);
462 GNUNET_free (tmp);
463 tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(ticket->rnd));
464 value = json_string (tmp);
465 json_object_set_new (json_resource, "rnd", value);
466 GNUNET_free (tmp);
467 GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it);
468}
469
470
471static void
472add_credential_cont (struct GNUNET_REST_RequestHandle *con_handle,
473 const char *url,
474 void *cls)
475{
476 struct RequestHandle *handle = cls;
477 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
478 const char *identity;
479 struct EgoEntry *ego_entry;
480 struct GNUNET_RECLAIM_Credential *attribute;
481 struct GNUNET_TIME_Relative exp;
482 char term_data[handle->rest_handle->data_size + 1];
483 json_t *data_json;
484 json_error_t err;
485 struct GNUNET_JSON_Specification attrspec[] =
486 { GNUNET_RECLAIM_JSON_spec_credential (&attribute),
487 GNUNET_JSON_spec_end () };
488
489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490 "Adding an credential for %s.\n",
491 handle->url);
492 if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen (
493 handle->url))
494 {
495 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
496 GNUNET_SCHEDULER_add_now (&do_error, handle);
497 return;
498 }
499 identity = handle->url + strlen (
500 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1;
501
502 for (ego_entry = ego_head; NULL != ego_entry;
503 ego_entry = ego_entry->next)
504 if (0 == strcmp (identity, ego_entry->identifier))
505 break;
506
507 if (NULL == ego_entry)
508 {
509 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown (%s)\n", identity);
510 return;
511 }
512 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
513
514 if (0 >= handle->rest_handle->data_size)
515 {
516 GNUNET_SCHEDULER_add_now (&do_error, handle);
517 return;
518 }
519
520 term_data[handle->rest_handle->data_size] = '\0';
521 GNUNET_memcpy (term_data,
522 handle->rest_handle->data,
523 handle->rest_handle->data_size);
524 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
525 if (GNUNET_OK != GNUNET_JSON_parse (data_json, attrspec, NULL, NULL))
526 {
527 json_decref (data_json);
528 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
529 "Unable to parse JSON from %s\n",
530 term_data);
531 GNUNET_SCHEDULER_add_now (&do_error, handle);
532 return;
533 }
534 json_decref (data_json);
535 if (NULL == attribute)
536 {
537 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
538 "Unable to parse credential from %s\n",
539 term_data);
540 GNUNET_SCHEDULER_add_now (&do_error, handle);
541 return;
542 }
543 /**
544 * New ID for attribute
545 */
546 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attribute->id))
547 GNUNET_RECLAIM_id_generate (&attribute->id);
548 exp = GNUNET_TIME_UNIT_HOURS;
549 handle->idp_op = GNUNET_RECLAIM_credential_store (idp,
550 identity_priv,
551 attribute,
552 &exp,
553 &finished_cont,
554 handle);
555 GNUNET_JSON_parse_free (attrspec);
556}
557
558
559/**
560 * Collect all credentials for an ego
561 *
562 */
563static void
564cred_collect (void *cls,
565 const struct GNUNET_CRYPTO_PublicKey *identity,
566 const struct GNUNET_RECLAIM_Credential *cred)
567{
568 struct RequestHandle *handle = cls;
569 struct GNUNET_RECLAIM_AttributeList *attrs;
570 struct GNUNET_RECLAIM_AttributeListEntry *ale;
571 struct GNUNET_TIME_Absolute exp;
572 json_t *attr_obj;
573 json_t *cred_obj;
574 const char *type;
575 char *tmp_value;
576 char *id_str;
577 char *issuer;
578
579
580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding credential: %s\n",
581 cred->name);
582 attrs = GNUNET_RECLAIM_credential_get_attributes (cred);
583 issuer = GNUNET_RECLAIM_credential_get_issuer (cred);
584 tmp_value = GNUNET_RECLAIM_credential_value_to_string (cred->type,
585 cred->data,
586 cred->data_size);
587 cred_obj = json_object ();
588 json_object_set_new (cred_obj, "value", json_string (tmp_value));
589 json_object_set_new (cred_obj, "name", json_string (cred->name));
590 type = GNUNET_RECLAIM_credential_number_to_typename (cred->type);
591 json_object_set_new (cred_obj, "type", json_string (type));
592 if (NULL != issuer)
593 {
594 json_object_set_new (cred_obj, "issuer", json_string (issuer));
595 GNUNET_free (issuer);
596 }
597 if (GNUNET_OK == GNUNET_RECLAIM_credential_get_expiration (cred,
598 &exp))
599 {
600 json_object_set_new (cred_obj, "expiration", json_integer (
601 exp.abs_value_us));
602 }
603 id_str = GNUNET_STRINGS_data_to_string_alloc (&cred->id,
604 sizeof(cred->id));
605 json_object_set_new (cred_obj, "id", json_string (id_str));
606 GNUNET_free (tmp_value);
607 GNUNET_free (id_str);
608 if (NULL != attrs)
609 {
610 json_t *attr_arr = json_array ();
611 for (ale = attrs->list_head; NULL != ale; ale = ale->next)
612 {
613 tmp_value =
614 GNUNET_RECLAIM_attribute_value_to_string (ale->attribute->type,
615 ale->attribute->data,
616 ale->attribute->data_size);
617 attr_obj = json_object ();
618 json_object_set_new (attr_obj, "value", json_string (tmp_value));
619 json_object_set_new (attr_obj, "name", json_string (
620 ale->attribute->name));
621
622 json_object_set_new (attr_obj, "flag", json_string ("1")); // FIXME
623 type = GNUNET_RECLAIM_attribute_number_to_typename (ale->attribute->type);
624 json_object_set_new (attr_obj, "type", json_string (type));
625 json_object_set_new (attr_obj, "id", json_string (""));
626 json_object_set_new (attr_obj, "credential", json_string (""));
627 json_array_append_new (attr_arr, attr_obj);
628 GNUNET_free (tmp_value);
629 }
630 json_object_set_new (cred_obj, "attributes", attr_arr);
631 }
632 json_array_append_new (handle->resp_object, cred_obj);
633 if (NULL != attrs)
634 GNUNET_RECLAIM_attribute_list_destroy (attrs);
635 GNUNET_RECLAIM_get_credentials_next (handle->cred_it);
636}
637
638
639/**
640 * Lists credential for identity request
641 *
642 * @param con_handle the connection handle
643 * @param url the url
644 * @param cls the RequestHandle
645 */
646static void
647list_credential_cont (struct GNUNET_REST_RequestHandle *con_handle,
648 const char *url,
649 void *cls)
650{
651 struct RequestHandle *handle = cls;
652 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
653 struct EgoEntry *ego_entry;
654 char *identity;
655
656 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
657 "Getting credentials for %s.\n",
658 handle->url);
659 if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen (
660 handle->url))
661 {
662 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
663 GNUNET_SCHEDULER_add_now (&do_error, handle);
664 return;
665 }
666 identity = handle->url + strlen (
667 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1;
668
669 for (ego_entry = ego_head; NULL != ego_entry;
670 ego_entry = ego_entry->next)
671 if (0 == strcmp (identity, ego_entry->identifier))
672 break;
673 handle->resp_object = json_array ();
674
675
676 if (NULL == ego_entry)
677 {
678 // Done
679 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
680 GNUNET_SCHEDULER_add_now (&return_response, handle);
681 return;
682 }
683 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
684 handle->cred_it = GNUNET_RECLAIM_get_credentials_start (idp,
685 priv_key,
686 &collect_error_cb,
687 handle,
688 &cred_collect,
689 handle,
690 &
691 collect_finished_cb,
692 handle);
693}
694
695
696/**
697 * Deletes credential from an identity
698 *
699 * @param con_handle the connection handle
700 * @param url the url
701 * @param cls the RequestHandle
702 */
703static void
704delete_credential_cont (struct GNUNET_REST_RequestHandle *con_handle,
705 const char *url,
706 void *cls)
707{
708 struct RequestHandle *handle = cls;
709 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
710 struct GNUNET_RECLAIM_Credential attr;
711 struct EgoEntry *ego_entry;
712 char *identity_id_str;
713 char *identity;
714 char *id;
715
716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting credential.\n");
717 if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen (
718 handle->url))
719 {
720 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
721 GNUNET_SCHEDULER_add_now (&do_error, handle);
722 return;
723 }
724 identity_id_str =
725 strdup (handle->url + strlen (
726 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1);
727 identity = strtok (identity_id_str, "/");
728 id = strtok (NULL, "/");
729 if ((NULL == identity) || (NULL == id))
730 {
731 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed request.\n");
732 GNUNET_free (identity_id_str);
733 GNUNET_SCHEDULER_add_now (&do_error, handle);
734 return;
735 }
736
737 for (ego_entry = ego_head; NULL != ego_entry;
738 ego_entry = ego_entry->next)
739 if (0 == strcmp (identity, ego_entry->identifier))
740 break;
741 handle->resp_object = json_array ();
742 if (NULL == ego_entry)
743 {
744 // Done
745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
746 GNUNET_free (identity_id_str);
747 GNUNET_SCHEDULER_add_now (&return_response, handle);
748 return;
749 }
750 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
751 memset (&attr, 0, sizeof(struct GNUNET_RECLAIM_Credential));
752 GNUNET_STRINGS_string_to_data (id, strlen (id), &attr.id, sizeof(attr.id));
753 attr.name = "";
754 handle->idp_op = GNUNET_RECLAIM_credential_delete (idp,
755 priv_key,
756 &attr,
757 &delete_finished_cb,
758 handle);
759 GNUNET_free (identity_id_str);
760}
761
762
763/**
764 * List tickets for identity request
765 *
766 * @param con_handle the connection handle
767 * @param url the url
768 * @param cls the RequestHandle
769 */
770static void
771list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
772 const char *url,
773 void *cls)
774{
775 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
776 struct RequestHandle *handle = cls;
777 struct EgoEntry *ego_entry;
778 char *identity;
779
780 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781 "Getting tickets for %s.\n",
782 handle->url);
783 if (strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >= strlen (handle->url))
784 {
785 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
786 GNUNET_SCHEDULER_add_now (&do_error, handle);
787 return;
788 }
789 identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
790
791 for (ego_entry = ego_head; NULL != ego_entry;
792 ego_entry = ego_entry->next)
793 if (0 == strcmp (identity, ego_entry->identifier))
794 break;
795 handle->resp_object = json_array ();
796
797 if (NULL == ego_entry)
798 {
799 // Done
800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
801 GNUNET_SCHEDULER_add_now (&return_response, handle);
802 return;
803 }
804 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
805 handle->ticket_it =
806 GNUNET_RECLAIM_ticket_iteration_start (idp,
807 priv_key,
808 &collect_error_cb,
809 handle,
810 &ticket_collect,
811 handle,
812 &collect_finished_cb,
813 handle);
814}
815
816
817static void
818add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
819 const char *url,
820 void *cls)
821{
822 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
823 const char *identity;
824 struct RequestHandle *handle = cls;
825 struct EgoEntry *ego_entry;
826 struct GNUNET_RECLAIM_Attribute *attribute;
827 struct GNUNET_TIME_Relative exp;
828 char term_data[handle->rest_handle->data_size + 1];
829 json_t *data_json;
830 json_error_t err;
831 struct GNUNET_JSON_Specification attrspec[] =
832 { GNUNET_RECLAIM_JSON_spec_attribute (&attribute), GNUNET_JSON_spec_end () };
833
834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835 "Adding an attribute for %s.\n",
836 handle->url);
837 if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url))
838 {
839 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
840 GNUNET_SCHEDULER_add_now (&do_error, handle);
841 return;
842 }
843 identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
844
845 for (ego_entry = ego_head; NULL != ego_entry;
846 ego_entry = ego_entry->next)
847 if (0 == strcmp (identity, ego_entry->identifier))
848 break;
849
850 if (NULL == ego_entry)
851 {
852 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown (%s)\n", identity);
853 return;
854 }
855 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
856
857 if (0 >= handle->rest_handle->data_size)
858 {
859 GNUNET_SCHEDULER_add_now (&do_error, handle);
860 return;
861 }
862
863 term_data[handle->rest_handle->data_size] = '\0';
864 GNUNET_memcpy (term_data,
865 handle->rest_handle->data,
866 handle->rest_handle->data_size);
867 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
868 GNUNET_assert (GNUNET_OK ==
869 GNUNET_JSON_parse (data_json, attrspec, NULL, NULL));
870 json_decref (data_json);
871 if (NULL == attribute)
872 {
873 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
874 "Unable to parse attribute from %s\n",
875 term_data);
876 GNUNET_SCHEDULER_add_now (&do_error, handle);
877 return;
878 }
879 /**
880 * New ID for attribute
881 */
882 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attribute->id))
883 GNUNET_RECLAIM_id_generate (&attribute->id);
884 exp = GNUNET_TIME_UNIT_HOURS;
885 handle->idp_op = GNUNET_RECLAIM_attribute_store (idp,
886 identity_priv,
887 attribute,
888 &exp,
889 &finished_cont,
890 handle);
891 GNUNET_JSON_parse_free (attrspec);
892}
893
894
895/**
896 * Parse a JWT and return the respective claim value as Attribute
897 *
898 * @param cred the jwt credential
899 * @param claim the name of the claim in the JWT
900 *
901 * @return a GNUNET_RECLAIM_Attribute, containing the new value
902 */
903struct GNUNET_RECLAIM_Attribute *
904parse_jwt (const struct GNUNET_RECLAIM_Credential *cred,
905 const char *claim)
906{
907 char *jwt_string;
908 struct GNUNET_RECLAIM_Attribute *attr;
909 char delim[] = ".";
910 const char *type_str = NULL;
911 const char *val_str = NULL;
912 char *data;
913 size_t data_size;
914 uint32_t type;
915 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n");
916 char *decoded_jwt;
917 json_t *json_val;
918 json_error_t *json_err = NULL;
919
920 jwt_string = GNUNET_RECLAIM_credential_value_to_string (cred->type,
921 cred->data,
922 cred->data_size);
923 char *jwt_body = strtok (jwt_string, delim);
924 jwt_body = strtok (NULL, delim);
925 GNUNET_STRINGS_base64_decode (jwt_body, strlen (jwt_body),
926 (void **) &decoded_jwt);
927 json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err);
928 const char *key;
929 json_t *value;
930 json_object_foreach (json_val, key, value) {
931 if (0 == strcasecmp (key,claim))
932 {
933 val_str = json_dumps (value, JSON_ENCODE_ANY);
934 }
935 }
936 type_str = "String";
937 type = GNUNET_RECLAIM_attribute_typename_to_number (type_str);
938 if (GNUNET_SYSERR == GNUNET_RECLAIM_attribute_string_to_value (type,val_str,
939 (void **) &data,
940 &data_size))
941 {
942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943 "Attribute value from JWT Parser invalid!\n");
944 GNUNET_RECLAIM_attribute_string_to_value (type,
945 "Error: Referenced Claim Name not Found",
946 (void **) &data,
947 &data_size);
948 attr = GNUNET_RECLAIM_attribute_new (claim, &cred->id,
949 type, data, data_size);
950 attr->id = cred->id;
951 attr->flag = 1;
952 }
953 else
954 {
955 attr = GNUNET_RECLAIM_attribute_new (claim, &cred->id,
956 type, data, data_size);
957 attr->id = cred->id;
958 attr->flag = 1;
959 }
960 return attr;
961}
962
963
964/**
965 * Collect all attributes for an ego
966 *
967 */
968static void
969attr_collect (void *cls,
970 const struct GNUNET_CRYPTO_PublicKey *identity,
971 const struct GNUNET_RECLAIM_Attribute *attr)
972{
973 struct RequestHandle *handle = cls;
974 json_t *attr_obj;
975 const char *type;
976 char *id_str;
977
978 char *tmp_value;
979 tmp_value = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
980 attr->data,
981 attr->data_size);
982 attr_obj = json_object ();
983 json_object_set_new (attr_obj, "value", json_string (tmp_value));
984 json_object_set_new (attr_obj, "name", json_string (attr->name));
985
986 if (GNUNET_RECLAIM_id_is_zero (&attr->credential))
987 json_object_set_new (attr_obj, "flag", json_string ("0"));
988 else
989 json_object_set_new (attr_obj, "flag", json_string ("1"));
990 type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type);
991 json_object_set_new (attr_obj, "type", json_string (type));
992 id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->id,
993 sizeof(attr->id));
994 json_object_set_new (attr_obj, "id", json_string (id_str));
995 GNUNET_free (id_str);
996 id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->credential,
997 sizeof(attr->credential));
998 json_object_set_new (attr_obj, "credential", json_string (id_str));
999 GNUNET_free (id_str);
1000 json_array_append (handle->resp_object, attr_obj);
1001 json_decref (attr_obj);
1002 GNUNET_free (tmp_value);
1003 GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1004}
1005
1006
1007/**
1008 * List attributes for identity request
1009 *
1010 * @param con_handle the connection handle
1011 * @param url the url
1012 * @param cls the RequestHandle
1013 */
1014static void
1015list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
1016 const char *url,
1017 void *cls)
1018{
1019 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
1020 struct RequestHandle *handle = cls;
1021 struct EgoEntry *ego_entry;
1022 char *identity;
1023
1024 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1025 "Getting attributes for %s.\n",
1026 handle->url);
1027 if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url))
1028 {
1029 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
1030 GNUNET_SCHEDULER_add_now (&do_error, handle);
1031 return;
1032 }
1033 identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
1034
1035 for (ego_entry = ego_head; NULL != ego_entry;
1036 ego_entry = ego_entry->next)
1037 if (0 == strcmp (identity, ego_entry->identifier))
1038 break;
1039 handle->resp_object = json_array ();
1040
1041
1042 if (NULL == ego_entry)
1043 {
1044 // Done
1045 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
1046 GNUNET_SCHEDULER_add_now (&return_response, handle);
1047 return;
1048 }
1049 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1050 handle->attr_it = GNUNET_RECLAIM_get_attributes_start (idp,
1051 priv_key,
1052 &collect_error_cb,
1053 handle,
1054 &attr_collect,
1055 handle,
1056 &collect_finished_cb,
1057 handle);
1058}
1059
1060
1061/**
1062 * List attributes for identity request
1063 *
1064 * @param con_handle the connection handle
1065 * @param url the url
1066 * @param cls the RequestHandle
1067 */
1068static void
1069delete_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
1070 const char *url,
1071 void *cls)
1072{
1073 const struct GNUNET_CRYPTO_PrivateKey *priv_key;
1074 struct RequestHandle *handle = cls;
1075 struct GNUNET_RECLAIM_Attribute attr;
1076 struct EgoEntry *ego_entry;
1077 char *identity_id_str;
1078 char *identity;
1079 char *id;
1080
1081 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting attributes.\n");
1082 if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url))
1083 {
1084 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
1085 GNUNET_SCHEDULER_add_now (&do_error, handle);
1086 return;
1087 }
1088 identity_id_str =
1089 strdup (handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1);
1090 identity = strtok (identity_id_str, "/");
1091 id = strtok (NULL, "/");
1092 if ((NULL == identity) || (NULL == id))
1093 {
1094 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed request.\n");
1095 GNUNET_free (identity_id_str);
1096 GNUNET_SCHEDULER_add_now (&do_error, handle);
1097 return;
1098 }
1099
1100 for (ego_entry = ego_head; NULL != ego_entry;
1101 ego_entry = ego_entry->next)
1102 if (0 == strcmp (identity, ego_entry->identifier))
1103 break;
1104 handle->resp_object = json_array ();
1105 if (NULL == ego_entry)
1106 {
1107 // Done
1108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
1109 GNUNET_free (identity_id_str);
1110 GNUNET_SCHEDULER_add_now (&return_response, handle);
1111 return;
1112 }
1113 priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1114 memset (&attr, 0, sizeof(struct GNUNET_RECLAIM_Attribute));
1115 GNUNET_STRINGS_string_to_data (id, strlen (id), &attr.id, sizeof(attr.id));
1116 attr.name = "";
1117 handle->idp_op = GNUNET_RECLAIM_attribute_delete (idp,
1118 priv_key,
1119 &attr,
1120 &delete_finished_cb,
1121 handle);
1122 GNUNET_free (identity_id_str);
1123}
1124
1125
1126static void
1127revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
1128 const char *url,
1129 void *cls)
1130{
1131 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
1132 struct RequestHandle *handle = cls;
1133 struct EgoEntry *ego_entry;
1134 struct GNUNET_RECLAIM_Ticket *ticket = NULL;
1135 struct GNUNET_CRYPTO_PublicKey tmp_pk;
1136 char term_data[handle->rest_handle->data_size + 1];
1137 json_t *data_json;
1138 json_error_t err;
1139 struct GNUNET_JSON_Specification tktspec[] =
1140 { GNUNET_RECLAIM_JSON_spec_ticket (&ticket), GNUNET_JSON_spec_end () };
1141
1142 if (0 >= handle->rest_handle->data_size)
1143 {
1144 GNUNET_SCHEDULER_add_now (&do_error, handle);
1145 return;
1146 }
1147
1148 term_data[handle->rest_handle->data_size] = '\0';
1149 GNUNET_memcpy (term_data,
1150 handle->rest_handle->data,
1151 handle->rest_handle->data_size);
1152 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
1153 if ((NULL == data_json) ||
1154 (GNUNET_OK != GNUNET_JSON_parse (data_json, tktspec, NULL, NULL)))
1155 {
1156 handle->emsg = GNUNET_strdup ("Not a ticket!\n");
1157 GNUNET_SCHEDULER_add_now (&do_error, handle);
1158 GNUNET_JSON_parse_free (tktspec);
1159 if (NULL != data_json)
1160 json_decref (data_json);
1161 return;
1162 }
1163 json_decref (data_json);
1164 if (NULL == ticket)
1165 {
1166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1167 "Unable to parse ticket from %s\n",
1168 term_data);
1169 GNUNET_SCHEDULER_add_now (&do_error, handle);
1170 return;
1171 }
1172
1173 for (ego_entry = ego_head; NULL != ego_entry;
1174 ego_entry = ego_entry->next)
1175 {
1176 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &tmp_pk);
1177 if (0 == memcmp (&ticket->identity,
1178 &tmp_pk,
1179 sizeof(struct GNUNET_CRYPTO_PublicKey)))
1180 break;
1181 }
1182 if (NULL == ego_entry)
1183 {
1184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown\n");
1185 GNUNET_JSON_parse_free (tktspec);
1186 return;
1187 }
1188 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1189
1190 handle->idp_op = GNUNET_RECLAIM_ticket_revoke (idp,
1191 identity_priv,
1192 ticket,
1193 &finished_cont,
1194 handle);
1195 GNUNET_JSON_parse_free (tktspec);
1196}
1197
1198
1199static void
1200consume_cont (void *cls,
1201 const struct GNUNET_CRYPTO_PublicKey *identity,
1202 const struct GNUNET_RECLAIM_Attribute *attr,
1203 const struct GNUNET_RECLAIM_Presentation *presentation)
1204{
1205 struct RequestHandle *handle = cls;
1206 char *val_str;
1207 json_t *value;
1208
1209 if (NULL == identity)
1210 {
1211 GNUNET_SCHEDULER_add_now (&return_response, handle);
1212 return;
1213 }
1214
1215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", attr->name);
1216 val_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
1217 attr->data,
1218 attr->data_size);
1219 if (NULL == val_str)
1220 {
1221 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1222 "Failed to parse value for: %s\n",
1223 attr->name);
1224 return;
1225 }
1226 value = json_string (val_str);
1227 json_object_set_new (handle->resp_object, attr->name, value);
1228 json_decref (value);
1229 GNUNET_free (val_str);
1230}
1231
1232
1233static void
1234consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
1235 const char *url,
1236 void *cls)
1237{
1238 const struct GNUNET_CRYPTO_PrivateKey *identity_priv;
1239 struct RequestHandle *handle = cls;
1240 struct EgoEntry *ego_entry;
1241 struct GNUNET_RECLAIM_Ticket *ticket;
1242 struct GNUNET_CRYPTO_PublicKey tmp_pk;
1243 char term_data[handle->rest_handle->data_size + 1];
1244 json_t *data_json;
1245 json_error_t err;
1246 struct GNUNET_JSON_Specification tktspec[] =
1247 { GNUNET_RECLAIM_JSON_spec_ticket (&ticket), GNUNET_JSON_spec_end () };
1248
1249 if (0 >= handle->rest_handle->data_size)
1250 {
1251 GNUNET_SCHEDULER_add_now (&do_error, handle);
1252 return;
1253 }
1254
1255 term_data[handle->rest_handle->data_size] = '\0';
1256 GNUNET_memcpy (term_data,
1257 handle->rest_handle->data,
1258 handle->rest_handle->data_size);
1259 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
1260 if (NULL == data_json)
1261 {
1262 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1263 "Unable to parse JSON Object from %s\n",
1264 term_data);
1265 GNUNET_SCHEDULER_add_now (&do_error, handle);
1266 return;
1267 }
1268 if (GNUNET_OK != GNUNET_JSON_parse (data_json, tktspec, NULL, NULL))
1269 {
1270 handle->emsg = GNUNET_strdup ("Not a ticket!\n");
1271 GNUNET_SCHEDULER_add_now (&do_error, handle);
1272 GNUNET_JSON_parse_free (tktspec);
1273 json_decref (data_json);
1274 return;
1275 }
1276 for (ego_entry = ego_head; NULL != ego_entry;
1277 ego_entry = ego_entry->next)
1278 {
1279 GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &tmp_pk);
1280 if (0 == memcmp (&ticket->audience,
1281 &tmp_pk,
1282 sizeof(struct GNUNET_CRYPTO_PublicKey)))
1283 break;
1284 }
1285 if (NULL == ego_entry)
1286 {
1287 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown\n");
1288 GNUNET_JSON_parse_free (tktspec);
1289 return;
1290 }
1291 identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1292 handle->resp_object = json_object ();
1293 handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp,
1294 identity_priv,
1295 ticket,
1296 &consume_cont,
1297 handle);
1298 GNUNET_JSON_parse_free (tktspec);
1299}
1300
1301
1302/**
1303 * Respond to OPTIONS request
1304 *
1305 * @param con_handle the connection handle
1306 * @param url the url
1307 * @param cls the RequestHandle
1308 */
1309static void
1310options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1311 const char *url,
1312 void *cls)
1313{
1314 struct MHD_Response *resp;
1315 struct RequestHandle *handle = cls;
1316
1317 // For now, independent of path return all options
1318 resp = GNUNET_REST_create_response (NULL);
1319 GNUNET_assert (MHD_NO != MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods));
1320 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1321 cleanup_handle (handle);
1322 return;
1323}
1324
1325
1326/**
1327 * If listing is enabled, prints information about the egos.
1328 *
1329 * This function is initially called for all egos and then again
1330 * whenever a ego's identifier changes or if it is deleted. At the
1331 * end of the initial pass over all egos, the function is once called
1332 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1333 * be invoked in the future or that there was an error.
1334 *
1335 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get', this
1336 * function is only called ONCE, and 'NULL' being passed in 'ego' does
1337 * indicate an error (for example because name is taken or no default value is
1338 * known). If 'ego' is non-NULL and if '*ctx' is set in those callbacks, the
1339 * value WILL be passed to a subsequent call to the identity callback of
1340 * 'GNUNET_IDENTITY_connect' (if that one was not NULL).
1341 *
1342 * When an identity is renamed, this function is called with the
1343 * (known) ego but the NEW identifier.
1344 *
1345 * When an identity is deleted, this function is called with the
1346 * (known) ego and "NULL" for the 'identifier'. In this case,
1347 * the 'ego' is henceforth invalid (and the 'ctx' should also be
1348 * cleaned up).
1349 *
1350 * @param cls closure
1351 * @param ego ego handle
1352 * @param ctx context for application to store data for this ego
1353 * (during the lifetime of this process, initially NULL)
1354 * @param identifier identifier assigned by the user for this ego,
1355 * NULL if the user just deleted the ego and it
1356 * must thus no longer be used
1357 */
1358static void
1359list_ego (void *cls,
1360 struct GNUNET_IDENTITY_Ego *ego,
1361 void **ctx,
1362 const char *identifier)
1363{
1364 struct EgoEntry *ego_entry;
1365 struct GNUNET_CRYPTO_PublicKey pk;
1366
1367 if (NULL == ego)
1368 {
1369 state = ID_REST_STATE_POST_INIT;
1370 return;
1371 }
1372 if (ID_REST_STATE_INIT == state)
1373 {
1374 ego_entry = GNUNET_new (struct EgoEntry);
1375 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1376 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1377 ego_entry->ego = ego;
1378 ego_entry->identifier = GNUNET_strdup (identifier);
1379 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1380 ego_tail,
1381 ego_entry);
1382 }
1383 /* Ego renamed or added */
1384 if (identifier != NULL)
1385 {
1386 for (ego_entry = ego_head; NULL != ego_entry;
1387 ego_entry = ego_entry->next)
1388 {
1389 if (ego_entry->ego == ego)
1390 {
1391 /* Rename */
1392 GNUNET_free (ego_entry->identifier);
1393 ego_entry->identifier = GNUNET_strdup (identifier);
1394 break;
1395 }
1396 }
1397 if (NULL == ego_entry)
1398 {
1399 /* Add */
1400 ego_entry = GNUNET_new (struct EgoEntry);
1401 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1402 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1403 ego_entry->ego = ego;
1404 ego_entry->identifier = GNUNET_strdup (identifier);
1405 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1406 ego_tail,
1407 ego_entry);
1408 }
1409 }
1410 else
1411 {
1412 /* Delete */
1413 for (ego_entry = ego_head; NULL != ego_entry;
1414 ego_entry = ego_entry->next)
1415 {
1416 if (ego_entry->ego == ego)
1417 break;
1418 }
1419 if (NULL == ego_entry)
1420 return; /* Not found */
1421
1422 GNUNET_CONTAINER_DLL_remove (ego_head,
1423 ego_tail,
1424 ego_entry);
1425 GNUNET_free (ego_entry->identifier);
1426 GNUNET_free (ego_entry->keystring);
1427 GNUNET_free (ego_entry);
1428 return;
1429 }
1430
1431}
1432
1433
1434static enum GNUNET_GenericReturnValue
1435rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
1436 GNUNET_REST_ResultProcessor proc,
1437 void *proc_cls)
1438{
1439 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1440 struct GNUNET_REST_RequestHandlerError err;
1441 static const struct GNUNET_REST_RequestHandler handlers[] =
1442 { { MHD_HTTP_METHOD_GET,
1443 GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &list_attribute_cont },
1444 { MHD_HTTP_METHOD_POST,
1445 GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &add_attribute_cont },
1446 { MHD_HTTP_METHOD_DELETE,
1447 GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &delete_attribute_cont },
1448 { MHD_HTTP_METHOD_GET,
1449 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &list_credential_cont },
1450 { MHD_HTTP_METHOD_POST,
1451 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &add_credential_cont },
1452 { MHD_HTTP_METHOD_DELETE,
1453 GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &delete_credential_cont },
1454 { MHD_HTTP_METHOD_GET,
1455 GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont },
1456 { MHD_HTTP_METHOD_POST,
1457 GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont },
1458 { MHD_HTTP_METHOD_POST,
1459 GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont },
1460 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM, &options_cont },
1461 GNUNET_REST_HANDLER_END};
1462
1463 handle->response_code = 0;
1464 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1465 handle->proc_cls = proc_cls;
1466 handle->proc = proc;
1467 handle->rest_handle = rest_handle;
1468
1469 handle->url = GNUNET_strdup (rest_handle->url);
1470 if (handle->url[strlen (handle->url) - 1] == '/')
1471 handle->url[strlen (handle->url) - 1] = '\0';
1472 handle->timeout_task =
1473 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
1474 GNUNET_CONTAINER_DLL_insert (requests_head,
1475 requests_tail,
1476 handle);
1477 if (GNUNET_NO ==
1478 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
1479 {
1480 cleanup_handle (handle);
1481 return GNUNET_NO;
1482 }
1483
1484 return GNUNET_YES;
1485}
1486
1487
1488/**
1489 * Entry point for the plugin.
1490 *
1491 * @param cls Config info
1492 * @return NULL on error, otherwise the plugin context
1493 */
1494void *
1495libgnunet_plugin_rest_reclaim_init (void *cls)
1496{
1497 static struct Plugin plugin;
1498 struct GNUNET_REST_Plugin *api;
1499
1500 cfg = cls;
1501 if (NULL != plugin.cfg)
1502 return NULL; /* can only initialize once! */
1503 memset (&plugin, 0, sizeof(struct Plugin));
1504 plugin.cfg = cfg;
1505 api = GNUNET_new (struct GNUNET_REST_Plugin);
1506 api->cls = &plugin;
1507 api->name = GNUNET_REST_API_NS_RECLAIM;
1508 api->process_request = &rest_identity_process_request;
1509 GNUNET_asprintf (&allow_methods,
1510 "%s, %s, %s, %s, %s",
1511 MHD_HTTP_METHOD_GET,
1512 MHD_HTTP_METHOD_POST,
1513 MHD_HTTP_METHOD_PUT,
1514 MHD_HTTP_METHOD_DELETE,
1515 MHD_HTTP_METHOD_OPTIONS);
1516 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
1517 state = ID_REST_STATE_INIT;
1518 idp = GNUNET_RECLAIM_connect (cfg);
1519 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1520 _ ("Identity Provider REST API initialized\n"));
1521 return api;
1522}
1523
1524
1525/**
1526 * Exit point from the plugin.
1527 *
1528 * @param cls the plugin context (as returned by "init")
1529 * @return always NULL
1530 */
1531void *
1532libgnunet_plugin_rest_reclaim_done (void *cls)
1533{
1534 struct GNUNET_REST_Plugin *api = cls;
1535 struct Plugin *plugin = api->cls;
1536 struct RequestHandle *request;
1537 struct EgoEntry *ego_entry;
1538 struct EgoEntry *ego_tmp;
1539
1540 plugin->cfg = NULL;
1541 while (NULL != (request = requests_head))
1542 do_error (request);
1543 if (NULL != idp)
1544 GNUNET_RECLAIM_disconnect (idp);
1545 if (NULL != identity_handle)
1546 GNUNET_IDENTITY_disconnect (identity_handle);
1547 for (ego_entry = ego_head; NULL != ego_entry;)
1548 {
1549 ego_tmp = ego_entry;
1550 ego_entry = ego_entry->next;
1551 GNUNET_free (ego_tmp->identifier);
1552 GNUNET_free (ego_tmp->keystring);
1553 GNUNET_free (ego_tmp);
1554 }
1555
1556 GNUNET_free (allow_methods);
1557 GNUNET_free (api);
1558 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1559 "Identity Provider REST plugin is finished\n");
1560 return NULL;
1561}
1562
1563
1564/* end of plugin_rest_reclaim.c */
diff --git a/src/rest-plugin/rest/Makefile.am b/src/rest-plugin/rest/Makefile.am
deleted file mode 100644
index 4f7f834fb..000000000
--- a/src/rest-plugin/rest/Makefile.am
+++ /dev/null
@@ -1,37 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10if USE_COVERAGE
11 AM_CFLAGS = --coverage -O0
12 XLIBS = -lgcov
13endif
14
15plugin_LTLIBRARIES = \
16 libgnunet_plugin_rest_copying.la \
17 libgnunet_plugin_rest_config.la
18
19libgnunet_plugin_rest_copying_la_SOURCES = \
20 plugin_rest_copying.c
21libgnunet_plugin_rest_copying_la_LIBADD = \
22 $(top_builddir)/src/service/rest/libgnunetrest.la \
23 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
24 $(LTLIBINTL) $(MHD_LIBS)
25libgnunet_plugin_rest_copying_la_LDFLAGS = \
26 $(GN_PLUGIN_LDFLAGS)
27libgnunet_plugin_rest_copying_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
28
29libgnunet_plugin_rest_config_la_SOURCES = \
30 plugin_rest_config.c
31libgnunet_plugin_rest_config_la_LIBADD = \
32 $(top_builddir)/src/service/rest/libgnunetrest.la \
33 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \
34 $(LTLIBINTL) $(MHD_LIBS) -ljansson
35libgnunet_plugin_rest_config_la_LDFLAGS = \
36 $(GN_PLUGIN_LDFLAGS)
37libgnunet_plugin_rest_config_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
diff --git a/src/rest-plugin/rest/meson.build b/src/rest-plugin/rest/meson.build
deleted file mode 100644
index 0c7219327..000000000
--- a/src/rest-plugin/rest/meson.build
+++ /dev/null
@@ -1,18 +0,0 @@
1shared_module('gnunet_plugin_rest_config',
2 ['plugin_rest_config.c'],
3 dependencies: [libgnunetrest_dep,
4 libgnunetutil_dep,
5 json_dep,
6 mhd_dep],
7 include_directories: [incdir, configuration_inc],
8 install: true,
9 install_dir: get_option('libdir') / 'gnunet')
10shared_module('gnunet_plugin_rest_copying',
11 ['plugin_rest_copying.c'],
12 dependencies: [libgnunetrest_dep,
13 libgnunetutil_dep,
14 json_dep,
15 mhd_dep],
16 include_directories: [incdir, configuration_inc],
17 install: true,
18 install_dir: get_option('libdir') / 'gnunet')
diff --git a/src/rest-plugin/rest/plugin_rest_config.c b/src/rest-plugin/rest/plugin_rest_config.c
deleted file mode 100644
index 826188702..000000000
--- a/src/rest-plugin/rest/plugin_rest_config.c
+++ /dev/null
@@ -1,455 +0,0 @@
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 * @file gns/plugin_rest_config.c
23 * @brief REST plugin for configuration
24 *
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include <gnunet_rest_lib.h>
30#include <gnunet_util_lib.h>
31#include <jansson.h>
32
33#define GNUNET_REST_API_NS_CONFIG "/config"
34
35/**
36 * @brief struct returned by the initialization function of the plugin
37 */
38struct Plugin
39{
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
41};
42
43const struct GNUNET_CONFIGURATION_Handle *cfg;
44
45struct RequestHandle
46{
47 /**
48 * DLL
49 */
50 struct RequestHandle *next;
51
52 /**
53 * DLL
54 */
55 struct RequestHandle *prev;
56
57 /**
58 * Handle to rest request
59 */
60 struct GNUNET_REST_RequestHandle *rest_handle;
61
62 /**
63 * The plugin result processor
64 */
65 GNUNET_REST_ResultProcessor proc;
66
67 /**
68 * The closure of the result processor
69 */
70 void *proc_cls;
71
72 /**
73 * HTTP response code
74 */
75 int response_code;
76
77 /**
78 * The URL
79 */
80 char *url;
81};
82
83/**
84 * DLL
85 */
86static struct RequestHandle *requests_head;
87
88/**
89 * DLL
90 */
91static struct RequestHandle *requests_tail;
92
93
94/**
95 * Cleanup request handle.
96 *
97 * @param handle Handle to clean up
98 */
99static void
100cleanup_handle (struct RequestHandle *handle)
101{
102 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
103 if (NULL != handle->url)
104 GNUNET_free (handle->url);
105 GNUNET_CONTAINER_DLL_remove (requests_head,
106 requests_tail,
107 handle);
108 GNUNET_free (handle);
109}
110
111
112/**
113 * Task run on shutdown. Cleans up everything.
114 *
115 * @param cls unused
116 */
117static void
118do_error (void *cls)
119{
120 struct RequestHandle *handle = cls;
121 struct MHD_Response *resp;
122
123 resp = GNUNET_REST_create_response (NULL);
124 handle->proc (handle->proc_cls, resp, handle->response_code);
125 cleanup_handle (handle);
126}
127
128
129static void
130add_sections (void *cls,
131 const char *section,
132 const char *option,
133 const char *value)
134{
135 json_t *sections_obj = cls;
136 json_t *sec_obj;
137
138 sec_obj = json_object_get (sections_obj, section);
139 if (NULL != sec_obj)
140 {
141 json_object_set_new (sec_obj, option, json_string (value));
142 return;
143 }
144 sec_obj = json_object ();
145 json_object_set_new (sec_obj, option, json_string (value));
146 json_object_set_new (sections_obj, section, sec_obj);
147}
148
149
150static void
151add_section_contents (void *cls,
152 const char *section,
153 const char *option,
154 const char *value)
155{
156 json_t *section_obj = cls;
157
158 json_object_set_new (section_obj, option, json_string (value));
159}
160
161
162/**
163 * Handle rest request
164 *
165 * @param handle the lookup handle
166 */
167static void
168get_cont (struct GNUNET_REST_RequestHandle *con_handle,
169 const char *url,
170 void *cls)
171{
172 struct MHD_Response *resp;
173 struct RequestHandle *handle = cls;
174 const char *section;
175 char *response;
176 json_t *result;
177
178 if (strlen (GNUNET_REST_API_NS_CONFIG) > strlen (handle->url))
179 {
180 handle->response_code = MHD_HTTP_BAD_REQUEST;
181 GNUNET_SCHEDULER_add_now (&do_error, handle);
182 return;
183 }
184 if (strlen (GNUNET_REST_API_NS_CONFIG) == strlen (handle->url))
185 {
186 result = json_object ();
187 GNUNET_CONFIGURATION_iterate (cfg, &add_sections, result);
188 }
189 else
190 {
191 result = json_object ();
192 section = &handle->url[strlen (GNUNET_REST_API_NS_CONFIG) + 1];
193 GNUNET_CONFIGURATION_iterate_section_values (cfg,
194 section,
195 &add_section_contents,
196 result);
197 }
198 response = json_dumps (result, 0);
199 resp = GNUNET_REST_create_response (response);
200 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
201 "Content-Type",
202 "application/json"));
203 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
204 cleanup_handle (handle);
205 GNUNET_free (response);
206 json_decref (result);
207}
208
209
210struct GNUNET_CONFIGURATION_Handle *
211set_value (struct GNUNET_CONFIGURATION_Handle *config,
212 const char *section,
213 const char *option,
214 json_t *value)
215{
216 if (json_is_string (value))
217 GNUNET_CONFIGURATION_set_value_string (config, section, option,
218 json_string_value (value));
219 else if (json_is_number (value))
220 GNUNET_CONFIGURATION_set_value_number (config, section, option,
221 json_integer_value (value));
222 else if (json_is_null (value))
223 GNUNET_CONFIGURATION_set_value_string (config, section, option, NULL);
224 else if (json_is_true (value))
225 GNUNET_CONFIGURATION_set_value_string (config, section, option, "yes");
226 else if (json_is_false (value))
227 GNUNET_CONFIGURATION_set_value_string (config, section, option, "no");
228 else
229 return NULL;
230 return config; // for error handling (0 -> success, 1 -> error)
231}
232
233
234/**
235 * Handle REST POST request
236 *
237 * @param handle the lookup handle
238 */
239static void
240set_cont (struct GNUNET_REST_RequestHandle *con_handle,
241 const char *url,
242 void *cls)
243{
244 struct RequestHandle *handle = cls;
245 char term_data[handle->rest_handle->data_size + 1];
246 struct GNUNET_CONFIGURATION_Handle *out = GNUNET_CONFIGURATION_dup (cfg);
247
248 json_error_t err;
249 json_t *data_json;
250 const char *section;
251 const char *option;
252 json_t *sec_obj;
253 json_t *value;
254 char *cfg_fn;
255
256 // invalid url
257 if (strlen (GNUNET_REST_API_NS_CONFIG) > strlen (handle->url))
258 {
259 handle->response_code = MHD_HTTP_BAD_REQUEST;
260 GNUNET_SCHEDULER_add_now (&do_error, handle);
261 return;
262 }
263
264 // extract data from handle
265 term_data[handle->rest_handle->data_size] = '\0';
266 GNUNET_memcpy (term_data,
267 handle->rest_handle->data,
268 handle->rest_handle->data_size);
269 data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
270
271 if (NULL == data_json)
272 {
273 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
274 "Unable to parse JSON Object from %s\n",
275 term_data);
276 GNUNET_SCHEDULER_add_now (&do_error, handle);
277 return;
278 }
279
280 // POST /config => {<section> : {<option> : <value>}}
281 if (strlen (GNUNET_REST_API_NS_CONFIG) == strlen (handle->url)) // POST /config
282 {
283 // iterate over sections
284 json_object_foreach (data_json, section, sec_obj)
285 {
286 // iterate over options
287 json_object_foreach (sec_obj, option, value)
288 {
289 out = set_value (out, section, option, value);
290 if (NULL == out)
291 {
292 handle->response_code = MHD_HTTP_BAD_REQUEST;
293 GNUNET_SCHEDULER_add_now (&do_error, handle);
294 json_decref (data_json);
295 return;
296 }
297 }
298 }
299 }
300 else // POST /config/<section> => {<option> : <value>}
301 {
302 // extract the "<section>" part from the url
303 section = &handle->url[strlen (GNUNET_REST_API_NS_CONFIG) + 1];
304 // iterate over options
305 json_object_foreach (data_json, option, value)
306 {
307 out = set_value (out, section, option, value);
308 if (NULL == out)
309 {
310 handle->response_code = MHD_HTTP_BAD_REQUEST;
311 GNUNET_SCHEDULER_add_now (&do_error, handle);
312 json_decref (data_json);
313 return;
314 }
315 }
316 }
317 json_decref (data_json);
318
319
320 // get cfg file path
321 cfg_fn = NULL;
322 const char *xdg = getenv ("XDG_CONFIG_HOME");
323 if (NULL != xdg)
324 GNUNET_asprintf (&cfg_fn,
325 "%s%s%s",
326 xdg,
327 DIR_SEPARATOR_STR,
328 GNUNET_OS_project_data_get ()->config_file);
329 else
330 cfg_fn = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file);
331
332 GNUNET_CONFIGURATION_write (out, cfg_fn);
333 cfg = out;
334 handle->proc (handle->proc_cls,
335 GNUNET_REST_create_response (NULL),
336 MHD_HTTP_OK);
337 GNUNET_free (cfg_fn);
338 cleanup_handle (handle);
339}
340
341
342/**
343 * Handle rest request
344 *
345 * @param handle the lookup handle
346 */
347static void
348options_cont (struct GNUNET_REST_RequestHandle *con_handle,
349 const char *url,
350 void *cls)
351{
352 struct MHD_Response *resp;
353 struct RequestHandle *handle = cls;
354
355 resp = GNUNET_REST_create_response (NULL);
356 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
357 "Access-Control-Allow-Methods",
358 MHD_HTTP_METHOD_GET));
359 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
360 cleanup_handle (handle);
361}
362
363
364/**
365 * Function processing the REST call
366 *
367 * @param method HTTP method
368 * @param url URL of the HTTP request
369 * @param data body of the HTTP request (optional)
370 * @param data_size length of the body
371 * @param proc callback function for the result
372 * @param proc_cls closure for @a proc
373 * @return #GNUNET_OK if request accepted
374 */
375static enum GNUNET_GenericReturnValue
376rest_config_process_request (struct GNUNET_REST_RequestHandle *conndata_handle,
377 GNUNET_REST_ResultProcessor proc,
378 void *proc_cls)
379{
380 static const struct GNUNET_REST_RequestHandler handlers[] = {
381 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CONFIG, &get_cont },
382 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_CONFIG, &set_cont },
383 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CONFIG, &options_cont },
384 GNUNET_REST_HANDLER_END
385 };
386 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
387 struct GNUNET_REST_RequestHandlerError err;
388
389 handle->proc_cls = proc_cls;
390 handle->proc = proc;
391 handle->rest_handle = conndata_handle;
392 handle->url = GNUNET_strdup (conndata_handle->url);
393 if (handle->url[strlen (handle->url) - 1] == '/')
394 handle->url[strlen (handle->url) - 1] = '\0';
395 GNUNET_CONTAINER_DLL_insert (requests_head,
396 requests_tail,
397 handle);
398 if (GNUNET_NO ==
399 GNUNET_REST_handle_request (conndata_handle, handlers, &err, handle))
400 {
401 cleanup_handle (handle);
402 return GNUNET_NO;
403 }
404 return GNUNET_YES;
405}
406
407
408/**
409 * Entry point for the plugin.
410 *
411 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
412 * @return NULL on error, otherwise the plugin context
413 */
414void *
415libgnunet_plugin_rest_config_init (void *cls)
416{
417 static struct Plugin plugin;
418
419 cfg = cls;
420 struct GNUNET_REST_Plugin *api;
421
422 memset (&plugin, 0, sizeof(struct Plugin));
423 plugin.cfg = cfg;
424 api = GNUNET_new (struct GNUNET_REST_Plugin);
425 api->cls = &plugin;
426 api->name = GNUNET_REST_API_NS_CONFIG;
427 api->process_request = &rest_config_process_request;
428 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("CONFIG REST API initialized\n"));
429 return api;
430}
431
432
433/**
434 * Exit point from the plugin.
435 *
436 * @param cls the plugin context (as returned by "init")
437 * @return always NULL
438 */
439void *
440libgnunet_plugin_rest_config_done (void *cls)
441{
442 struct GNUNET_REST_Plugin *api = cls;
443 struct Plugin *plugin;
444
445 while (NULL != requests_head)
446 cleanup_handle (requests_head);
447 plugin = api->cls;
448 plugin->cfg = NULL;
449 GNUNET_free (api);
450 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CONFIG REST plugin is finished\n");
451 return NULL;
452}
453
454
455/* end of plugin_rest_config.c */
diff --git a/src/rest-plugin/rest/plugin_rest_copying.c b/src/rest-plugin/rest/plugin_rest_copying.c
deleted file mode 100644
index 52783a81a..000000000
--- a/src/rest-plugin/rest/plugin_rest_copying.c
+++ /dev/null
@@ -1,238 +0,0 @@
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 * @file gns/plugin_rest_copying.c
23 * @brief REST plugin that serves licensing information.
24 *
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include <gnunet_rest_lib.h>
30
31#define GNUNET_REST_API_NS_COPYING "/copying"
32
33#define GNUNET_REST_COPYING_TEXT \
34 "GNU Affero General Public License version 3 or later. See also: <http://www.gnu.org/licenses/>"
35
36/**
37 * @brief struct returned by the initialization function of the plugin
38 */
39struct Plugin
40{
41 const struct GNUNET_CONFIGURATION_Handle *cfg;
42};
43
44const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46struct RequestHandle
47{
48 /**
49 * DLL
50 */
51 struct RequestHandle *next;
52
53 /**
54 * DLL
55 */
56 struct RequestHandle *prev;
57
58 /**
59 * Handle to rest request
60 */
61 struct GNUNET_REST_RequestHandle *rest_handle;
62
63 /**
64 * The plugin result processor
65 */
66 GNUNET_REST_ResultProcessor proc;
67
68 /**
69 * The closure of the result processor
70 */
71 void *proc_cls;
72
73 /**
74 * HTTP response code
75 */
76 int response_code;
77};
78
79/**
80 * DLL
81 */
82static struct RequestHandle *requests_head;
83
84/**
85 * DLL
86 */
87static struct RequestHandle *requests_tail;
88
89/**
90 * Cleanup request handle.
91 *
92 * @param handle Handle to clean up
93 */
94static void
95cleanup_handle (struct RequestHandle *handle)
96{
97 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
98 "Cleaning up\n");
99 GNUNET_CONTAINER_DLL_remove (requests_head,
100 requests_tail,
101 handle);
102 GNUNET_free (handle);
103}
104
105
106/**
107 * Handle rest request
108 *
109 * @param handle the lookup handle
110 */
111static void
112get_cont (struct GNUNET_REST_RequestHandle *con_handle,
113 const char*url,
114 void *cls)
115{
116 struct MHD_Response *resp;
117 struct RequestHandle *handle = cls;
118
119 resp = GNUNET_REST_create_response (GNUNET_REST_COPYING_TEXT);
120 handle->proc (handle->proc_cls,
121 resp,
122 MHD_HTTP_OK);
123 cleanup_handle (handle);
124}
125
126
127/**
128 * Handle rest request
129 *
130 * @param handle the lookup handle
131 */
132static void
133options_cont (struct GNUNET_REST_RequestHandle *con_handle,
134 const char*url,
135 void *cls)
136{
137 struct MHD_Response *resp;
138 struct RequestHandle *handle = cls;
139
140 resp = GNUNET_REST_create_response (NULL);
141 GNUNET_assert (MHD_NO != MHD_add_response_header (resp,
142 "Access-Control-Allow-Methods",
143 MHD_HTTP_METHOD_GET));
144 handle->proc (handle->proc_cls,
145 resp,
146 MHD_HTTP_OK);
147 cleanup_handle (handle);
148}
149
150
151/**
152 * Function processing the REST call
153 *
154 * @param method HTTP method
155 * @param url URL of the HTTP request
156 * @param data body of the HTTP request (optional)
157 * @param data_size length of the body
158 * @param proc callback function for the result
159 * @param proc_cls closure for @a proc
160 * @return #GNUNET_OK if request accepted
161 */
162static enum GNUNET_GenericReturnValue
163rest_copying_process_request (struct GNUNET_REST_RequestHandle *conndata_handle,
164 GNUNET_REST_ResultProcessor proc,
165 void *proc_cls)
166{
167 static const struct GNUNET_REST_RequestHandler handlers[] = {
168 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_COPYING, &get_cont },
169 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_COPYING, &options_cont },
170 GNUNET_REST_HANDLER_END
171 };
172 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
173 struct GNUNET_REST_RequestHandlerError err;
174
175 handle->proc_cls = proc_cls;
176 handle->proc = proc;
177 handle->rest_handle = conndata_handle;
178 GNUNET_CONTAINER_DLL_insert (requests_head,
179 requests_tail,
180 handle);
181 return GNUNET_REST_handle_request (conndata_handle,
182 handlers,
183 &err,
184 handle);
185}
186
187
188/**
189 * Entry point for the plugin.
190 *
191 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
192 * @return NULL on error, otherwise the plugin context
193 */
194void *
195libgnunet_plugin_rest_copying_init (void *cls)
196{
197 static struct Plugin plugin;
198
199 cfg = cls;
200 struct GNUNET_REST_Plugin *api;
201
202 if (NULL != plugin.cfg)
203 return NULL; /* can only initialize once! */
204 memset (&plugin, 0, sizeof(struct Plugin));
205 plugin.cfg = cfg;
206 api = GNUNET_new (struct GNUNET_REST_Plugin);
207 api->cls = &plugin;
208 api->name = GNUNET_REST_API_NS_COPYING;
209 api->process_request = &rest_copying_process_request;
210 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211 _ ("COPYING REST API initialized\n"));
212 return api;
213}
214
215
216/**
217 * Exit point from the plugin.
218 *
219 * @param cls the plugin context (as returned by "init")
220 * @return always NULL
221 */
222void *
223libgnunet_plugin_rest_copying_done (void *cls)
224{
225 struct GNUNET_REST_Plugin *api = cls;
226 struct Plugin *plugin = api->cls;
227
228 while (NULL != requests_head)
229 cleanup_handle (requests_head);
230 plugin->cfg = NULL;
231 GNUNET_free (api);
232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
233 "COPYING REST plugin is finished\n");
234 return NULL;
235}
236
237
238/* end of plugin_rest_copying.c */