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