GNUnet  0.11.x
plugin_rest_pabc.c
Go to the documentation of this file.
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  */
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 
41 #define GNUNET_REST_API_NS_PABC "/pabc"
42 
46 #define GNUNET_REST_API_NS_PABC_CR "/pabc/cr"
47 
52 
56 static char *allow_methods;
57 
61 struct Plugin
62 {
63  const struct GNUNET_CONFIGURATION_Handle *cfg;
64 };
65 
66 
67 struct RequestHandle
68 {
72  struct RequestHandle *next;
73 
77  struct RequestHandle *prev;
78 
82  struct GNUNET_REST_RequestHandle *rest_handle;
83 
87  struct GNUNET_TIME_Relative timeout;
88 
93 
98 
102  void *proc_cls;
103 
107  char *url;
108 
112  char *emsg;
113 
117  int response_code;
118 
122  json_t *resp_object;
123 };
124 
129 
134 
135 
140 static void
141 cleanup_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)
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 
166 static void
167 do_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 
191 static void
192 do_timeout (void *cls)
193 {
194  struct RequestHandle *handle = cls;
195 
196  handle->timeout_task = NULL;
197  do_error (handle);
198 }
199 
200 
201 static void
202 return_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 
218 static enum pabc_status
219 set_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  {
270  "Unable to encode JSON value for `%s'\n", key);
271  continue;
272  }
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  {
282  "Failed to set attribute `%s'.\n", key);
283  }
284  }
285  return PABC_OK;
286 }
287 
288 
289 static enum GNUNET_GenericReturnValue
290 setup_new_user_context (struct pabc_context *ctx,
291  struct pabc_public_parameters *pp,
292  struct pabc_user_context **usr_ctx)
293 {
294  if (PABC_OK != pabc_new_user_context (ctx, pp, usr_ctx))
295  return GNUNET_SYSERR;
296 
297  if (PABC_OK != pabc_populate_user_context (ctx, *usr_ctx))
298  {
299  pabc_free_user_context (ctx, pp, usr_ctx);
300  return GNUNET_SYSERR;
301  }
302  return GNUNET_OK;
303 }
304 
305 
306 static void
307 cr_cont (struct GNUNET_REST_RequestHandle *con_handle,
308  const char *url,
309  void *cls)
310 {
311  struct RequestHandle *handle = cls;
312  char term_data[handle->rest_handle->data_size + 1];
313  char *response_str;
314  json_t *data_json;
315  json_t *nonce_json;
316  json_t *pp_json;
317  json_t *idtoken_json;
318  json_t *iss_json;
319  json_t *identity_json;
320  json_error_t err;
321  struct pabc_public_parameters *pp = NULL;
322  struct pabc_context *ctx = NULL;
323  struct pabc_user_context *usr_ctx = NULL;
324  struct pabc_credential_request *cr = NULL;
325  struct pabc_nonce *nonce = NULL;
326  enum pabc_status status;
327 
328 
330  "Credential request...\n");
331 
332  if (0 >= handle->rest_handle->data_size)
333  {
335  return;
336  }
337 
338  term_data[handle->rest_handle->data_size] = '\0';
339  GNUNET_memcpy (term_data,
340  handle->rest_handle->data,
341  handle->rest_handle->data_size);
342  data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
343  if (NULL == data_json)
344  {
346  "Unable to parse %s\n", term_data);
348  return;
349  }
350  if (! json_is_object (data_json))
351  {
353  "Unable to parse %s\n", term_data);
354  json_decref (data_json);
356  return;
357  }
358 
359  nonce_json = json_object_get (data_json, "nonce");
360  if (NULL == nonce_json)
361  {
363  "Unable to parse nonce\n");
364  json_decref (data_json);
366  return;
367  }
368  iss_json = json_object_get (data_json, "issuer");
369  if (NULL == iss_json)
370  {
372  "Unable to parse issuer\n");
373  json_decref (data_json);
375  return;
376  }
377  identity_json = json_object_get (data_json, "identity");
378  if (NULL == identity_json)
379  {
381  "Unable to parse identity\n");
382  json_decref (data_json);
384  return;
385  }
386  idtoken_json = json_object_get (data_json, "id_token");
387  if (NULL == idtoken_json)
388  {
390  "Unable to parse id_token\n");
391  json_decref (data_json);
393  return;
394  }
395  pp_json = json_object_get (data_json, "public_params");
396  if (NULL == pp_json)
397  {
399  "Unable to parse public parameters\n");
400  json_decref (data_json);
402  return;
403  }
404 
405  PABC_ASSERT (pabc_new_ctx (&ctx));
406  char *pp_str = json_dumps (pp_json, JSON_ENCODE_ANY);
407  status = pabc_decode_and_new_public_parameters (ctx,
408  &pp,
409  pp_str);
410  char *ppid;
411  GNUNET_assert (PABC_OK == pabc_cred_get_ppid_from_pp (pp_str, &ppid));
412  GNUNET_free (pp_str);
413  if (status != PABC_OK)
414  {
416  "Failed to read public parameters: %s\n",
417  pp_str);
418  json_decref (data_json);
420  return;
421  }
422  // (Over)write parameters
423  status = PABC_write_public_parameters (json_string_value (iss_json),
424  pp);
425  if (status != PABC_OK)
426  {
428  "Failed to write public parameters.\n");
429  json_decref (data_json);
431  return;
432  }
433  status = PABC_read_usr_ctx (json_string_value (identity_json),
434  json_string_value (iss_json),
435  ctx, pp, &usr_ctx);
436  if (PABC_OK != status)
437  {
438  if (GNUNET_OK != setup_new_user_context (ctx, pp, &usr_ctx))
439  {
440  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to setup user context.\n");
441  pabc_free_public_parameters (ctx, &pp);
442  json_decref (data_json);
444  return;
445  }
446  PABC_write_usr_ctx (json_string_value (identity_json),
447  json_string_value (iss_json),
448  ctx, pp, usr_ctx);
449  }
450 
451  // Set attributes from JWT to context
452  status = set_attributes_from_idtoken (ctx,
453  pp,
454  usr_ctx,
455  json_string_value (idtoken_json));
456  if (status != PABC_OK)
457  {
458  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to set attributes.\n");
459  pabc_free_user_context (ctx, pp, &usr_ctx);
460  pabc_free_public_parameters (ctx, &pp);
461  json_decref (data_json);
463  return;
464  }
465 
466 
467  // nonce
468  status = pabc_new_nonce (ctx, &nonce);
469  if (status != PABC_OK)
470  {
471  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate nonce.\n");
472  pabc_free_user_context (ctx, pp, &usr_ctx);
473  pabc_free_public_parameters (ctx, &pp);
474  json_decref (data_json);
476  return;
477  }
478  char *nonce_str = json_dumps (nonce_json, JSON_ENCODE_ANY);
479  status = pabc_decode_nonce (ctx, nonce, nonce_str);
480  if (status != PABC_OK)
481  {
482  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to decode nonce.\n");
483  pabc_free_nonce (ctx, &nonce);
484  pabc_free_user_context (ctx, pp, &usr_ctx);
485  pabc_free_public_parameters (ctx, &pp);
486  json_decref (data_json);
488  return;
489  }
490 
491  // cr
492  status = pabc_new_credential_request (ctx, pp, &cr);
493  if (PABC_OK != status)
494  {
495  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate cr.\n");
496  pabc_free_nonce (ctx, &nonce);
497  pabc_free_user_context (ctx, pp, &usr_ctx);
498  pabc_free_public_parameters (ctx, &pp);
499  json_decref (data_json);
501  return;
502  }
503 
504  status = pabc_gen_credential_request (ctx, pp, usr_ctx, nonce, cr);
505  if (PABC_OK != status)
506  {
507  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to generate cr.\n");
508  pabc_free_nonce (ctx, &nonce);
509  pabc_free_credential_request (ctx, pp, &cr);
510  pabc_free_user_context (ctx, pp, &usr_ctx);
511  pabc_free_public_parameters (ctx, &pp);
512  json_decref (data_json);
514  return;
515  }
516  handle->resp_object = json_object ();
517  GNUNET_assert (PABC_OK == pabc_cred_encode_cr (ctx, pp, cr,
518  json_string_value (
519  identity_json),
520  ppid, &response_str));
521  if (PABC_OK != status)
522  {
523  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to serialize cr.\n");
524  pabc_free_nonce (ctx, &nonce);
525  pabc_free_credential_request (ctx, pp, &cr);
526  pabc_free_user_context (ctx, pp, &usr_ctx);
527  pabc_free_public_parameters (ctx, &pp);
528  json_decref (data_json);
530  return;
531  }
532  json_decref (handle->resp_object);
533  handle->resp_object = json_loads (response_str, JSON_DECODE_ANY, &err);
534  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", response_str);
535  GNUNET_free (response_str);
536 
537  // clean up
538  pabc_free_nonce (ctx, &nonce);
539  pabc_free_credential_request (ctx, pp, &cr);
540  pabc_free_user_context (ctx, pp, &usr_ctx);
541  pabc_free_public_parameters (ctx, &pp);
543  json_decref (data_json);
544 }
545 
546 
554 static void
556  const char *url,
557  void *cls)
558 {
559  struct MHD_Response *resp;
560  struct RequestHandle *handle = cls;
561 
562  // For now, independent of path return all options
563  resp = GNUNET_REST_create_response (NULL);
564  MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
565  handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
566  cleanup_handle (handle);
567  return;
568 }
569 
570 
571 static enum GNUNET_GenericReturnValue
574  void *proc_cls)
575 {
576  struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
578  static const struct GNUNET_REST_RequestHandler handlers[] = {
579  {MHD_HTTP_METHOD_POST,
581  { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PABC, &options_cont },
583  };
584 
585  handle->response_code = 0;
587  handle->proc_cls = proc_cls;
588  handle->proc = proc;
589  handle->rest_handle = rest_handle;
590 
591  handle->url = GNUNET_strdup (rest_handle->url);
592  if (handle->url[strlen (handle->url) - 1] == '/')
593  handle->url[strlen (handle->url) - 1] = '\0';
594  handle->timeout_task =
595  GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
596  GNUNET_CONTAINER_DLL_insert (requests_head,
597  requests_tail,
598  handle);
599  if (GNUNET_NO ==
600  GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
601  {
602  cleanup_handle (handle);
603  return GNUNET_NO;
604  }
605 
606  return GNUNET_YES;
607 }
608 
609 
616 void *
618 {
619  static struct Plugin plugin;
620  struct GNUNET_REST_Plugin *api;
621 
622  cfg = cls;
623  if (NULL != plugin.cfg)
624  return NULL; /* can only initialize once! */
625  memset (&plugin, 0, sizeof(struct Plugin));
626  plugin.cfg = cfg;
627  api = GNUNET_new (struct GNUNET_REST_Plugin);
628  api->cls = &plugin;
632  "%s, %s",
633  MHD_HTTP_METHOD_POST,
634  MHD_HTTP_METHOD_OPTIONS);
636  _ ("Identity Provider REST API initialized\n"));
637  return api;
638 }
639 
640 
647 void *
649 {
650  struct GNUNET_REST_Plugin *api = cls;
651  struct Plugin *plugin = api->cls;
652  struct RequestHandle *request;
653 
654  plugin->cfg = NULL;
655  while (NULL != (request = requests_head))
656  do_error (request);
657 
659  GNUNET_free (api);
661  "PABC REST plugin is finished\n");
662  return NULL;
663 }
664 
665 
666 /* end of plugin_rest_reclaim.c */
#define GNUNET_CONTAINER_DLL_remove(head, tail, element)
Remove an element from a DLL.
void(* GNUNET_REST_ResultProcessor)(void *cls, struct MHD_Response *resp, int status)
Iterator called on obtained result for a REST result.
static struct GNUNET_VPN_RedirectionRequest * request
Opaque redirection request handle.
Definition: gnunet-vpn.c:41
#define GNUNET_CONTAINER_DLL_insert(head, tail, element)
Insert an element at the head of a DLL.
#define GNUNET_REST_API_NS_PABC
REST root namespace.
void * cls
The closure of the plugin.
size_t data_size
The POST data size.
GNUNET_REST_ResultProcessor proc
The plugin result processor.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
char * key
TLS key.
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
struct returned by the initialization function of the plugin
GNUNET_GenericReturnValue
Named constants for return values.
Definition: gnunet_common.h:83
#define GNUNET_new(type)
Allocate a struct or union of the given type.
void(* proc)(struct GNUNET_REST_RequestHandle *handle, const char *url, void *cls)
Namespace to handle.
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
void * proc_cls
The closure of the result processor.
static void cleanup_handle(void *cls)
Cleanup lookup handle.
#define GNUNET_REST_HANDLER_END
static struct GNUNET_DNSSTUB_Context * ctx
Context for DNS resolution.
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:47
The request handle.
#define _(String)
GNU gettext support macro.
Definition: platform.h:178
static enum GNUNET_GenericReturnValue rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle, GNUNET_REST_ResultProcessor proc, void *proc_cls)
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1269
static void options_cont(struct GNUNET_REST_RequestHandle *con_handle, const char *url, void *cls)
Respond to OPTIONS request.
json_t * resp_object
Response object.
static char * value
Value of the record to add/remove.
char * emsg
Error response message.
const char * url
The url as string.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1296
char * name
Plugin name.
static struct RequestHandle * requests_tail
DLL.
struct GNUNET_TIME_Relative timeout
Desired timeout for the lookup (default is no timeout).
static enum GNUNET_GenericReturnValue setup_new_user_context(struct pabc_context *ctx, struct pabc_public_parameters *pp, struct pabc_user_context **usr_ctx)
void * libgnunet_plugin_rest_reclaim_done(void *cls)
Exit point from the plugin.
uint16_t status
See PRISM_STATUS_*-constants.
static struct GNUNET_DNS_Handle * handle
Handle to transport service.
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
static enum pabc_status set_attributes_from_idtoken(const struct pabc_context *ctx, const struct pabc_public_parameters *pp, struct pabc_user_context *usr_ctx, const char *id_token)
int GNUNET_REST_handle_request(struct GNUNET_REST_RequestHandle *conn, const struct GNUNET_REST_RequestHandler *handlers, struct GNUNET_REST_RequestHandlerError *err, void *cls)
Definition: rest.c:77
struct GNUNET_REST_RequestHandle * rest_handle
Rest connection.
int response_code
Response code.
static void do_error(void *cls)
Task run on error, sends error message.
#define GNUNET_REST_API_NS_PABC_CR
Credential request endpoint.
static char * plugin
Solver plugin name as string.
static void do_timeout(void *cls)
Task run on timeout, sends error message.
void * libgnunet_plugin_rest_pabc_init(void *cls)
Entry point for the plugin.
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
const char * data
The POST data.
struct RequestHandle * next
DLL.
enum GNUNET_GenericReturnValue PABC_write_public_parameters(char const *const pp_name, struct pabc_public_parameters *const pp)
Definition: pabc_helper.c:169
const struct GNUNET_CONFIGURATION_Handle * cfg
The configuration handle.
struct RequestHandle * prev
DLL.
static struct GNUNET_SCHEDULER_Task * timeout_task
Task to be run on timeout.
Definition: gnunet-arm.c:124
static struct RequestHandle * requests_head
DLL.
enum GNUNET_GenericReturnValue PABC_write_usr_ctx(char const *const usr_name, char const *const pp_name, struct pabc_context const *const ctx, struct pabc_public_parameters const *const pp, struct pabc_user_context *const usr_ctx)
Definition: pabc_helper.c:221
#define GNUNET_strndup(a, length)
Wrapper around GNUNET_xstrndup_.
static void return_response(void *cls)
configuration data
Definition: configuration.c:84
Handle for a plugin.
Definition: block.c:37
struct GNUNET_SCHEDULER_Task * timeout_task
ID of a task associated with the resolution process.
struct MHD_Response * GNUNET_REST_create_response(const char *data)
Create REST MHD response.
Definition: rest.c:57
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:134
static void cr_cont(struct GNUNET_REST_RequestHandle *con_handle, const char *url, void *cls)
enum GNUNET_GenericReturnValue PABC_read_usr_ctx(char const *const usr_name, char const *const pp_name, struct pabc_context const *const ctx, struct pabc_public_parameters const *const pp, struct pabc_user_context **usr_ctx)
Definition: pabc_helper.c:293
static char * allow_methods
HTTP methods allows for this plugin.
enum GNUNET_GenericReturnValue(* process_request)(struct GNUNET_REST_RequestHandle *handle, GNUNET_REST_ResultProcessor proc, void *proc_cls)
Function to process a REST call.
size_t GNUNET_STRINGS_base64url_decode(const char *data, size_t len, void **out)
Decode from Base64url.
Definition: strings.c:2050
#define GNUNET_free(ptr)
Wrapper around free.
Time for relative time used by GNUnet, in microseconds.
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:972
char * url
The url.