-add serializer
[oweals/gnunet.git] / src / credential / plugin_rest_credential.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2016 GNUnet e.V.
4
5    GNUnet is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; either version 3, or (at your
8    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    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with GNUnet; see the file COPYING.  If not, write to the
17    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19    */
20 /**
21  * @author Martin Schanzenbach
22  * @file gns/plugin_rest_credential.c
23  * @brief GNUnet CREDENTIAL REST plugin
24  *
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include <gnunet_identity_service.h>
30 #include <gnunet_gnsrecord_lib.h>
31 #include <gnunet_namestore_service.h>
32 #include <gnunet_credential_service.h>
33 #include <gnunet_rest_lib.h>
34 #include <gnunet_jsonapi_lib.h>
35 #include <gnunet_jsonapi_util.h>
36 #include <jansson.h>
37
38 #define GNUNET_REST_API_NS_CREDENTIAL "/credential"
39
40 #define GNUNET_REST_JSONAPI_CREDENTIAL "credential"
41
42 #define GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO "credential"
43
44 #define GNUNET_REST_JSONAPI_CREDENTIAL_CHAIN "chain"
45
46 #define GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR "attribute"
47
48 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR "credential"
49
50 /**
51  * @brief struct returned by the initialization function of the plugin
52  */
53 struct Plugin
54 {
55   const struct GNUNET_CONFIGURATION_Handle *cfg;
56 };
57
58 const struct GNUNET_CONFIGURATION_Handle *cfg;
59
60 struct VerifyHandle
61 {
62   /**
63    * Handle to Credential service.
64    */
65   struct GNUNET_CREDENTIAL_Handle *credential;
66
67   /**
68    * Handle to lookup request
69    */
70   struct GNUNET_CREDENTIAL_Request *verify_request;
71
72   /**
73    * Handle to rest request
74    */
75   struct GNUNET_REST_RequestHandle *rest_handle;
76
77   /**
78    * ID of a task associated with the resolution process.
79    */
80   struct GNUNET_SCHEDULER_Task * timeout_task;
81
82   /**
83    * The root of the received JSON or NULL
84    */
85   json_t *json_root;
86
87   /**
88    * The plugin result processor
89    */
90   GNUNET_REST_ResultProcessor proc;
91
92   /**
93    * The closure of the result processor
94    */
95   void *proc_cls;
96
97   /**
98    * The issuer attribute to verify
99    */
100   char *issuer_attr;
101
102   /**
103    * The subject attribute
104    */
105   char *subject_attr;
106
107   /**
108    * The public key of the issuer
109    */
110   struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
111
112   /**
113    * The public key of the subject
114    */
115   struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
116
117   /**
118    * HTTP response code
119    */
120   int response_code;
121
122   /**
123    * Timeout
124    */
125   struct GNUNET_TIME_Relative timeout;
126
127 };
128
129
130 /**
131  * Cleanup lookup handle.
132  *
133  * @param handle Handle to clean up
134  */
135 static void
136 cleanup_handle (struct VerifyHandle *handle)
137 {
138   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
139               "Cleaning up\n");
140   if (NULL != handle->json_root)
141     json_decref (handle->json_root);
142
143   if (NULL != handle->issuer_attr)
144     GNUNET_free (handle->issuer_attr);
145   if (NULL != handle->subject_attr)
146     GNUNET_free (handle->subject_attr);
147   if (NULL != handle->verify_request)
148   {
149     GNUNET_CREDENTIAL_verify_cancel (handle->verify_request);
150     handle->verify_request = NULL;
151   }
152   if (NULL != handle->credential)
153   {
154     GNUNET_CREDENTIAL_disconnect (handle->credential);
155     handle->credential = NULL;
156   }
157
158   if (NULL != handle->timeout_task)
159   {
160     GNUNET_SCHEDULER_cancel (handle->timeout_task);
161   }
162   GNUNET_free (handle);
163 }
164
165
166 /**
167  * Task run on shutdown.  Cleans up everything.
168  *
169  * @param cls unused
170  * @param tc scheduler context
171  */
172 static void
173 do_error (void *cls)
174 {
175   struct VerifyHandle *handle = cls;
176   struct MHD_Response *resp;
177
178   resp = GNUNET_REST_create_response (NULL);
179   handle->proc (handle->proc_cls, resp, handle->response_code);
180   cleanup_handle (handle);
181 }
182
183 /**
184  * Attribute delegation to JSON
185  * @param attr the attribute
186  * @return JSON, NULL if failed
187  */
188 static json_t*
189 attribute_delegation_to_json (struct GNUNET_CREDENTIAL_Delegation *delegation_chain_entry)
190 {
191   char *subject;
192   char *issuer;
193   char iss_attribute[delegation_chain_entry->issuer_attribute_len];
194   char sub_attribute[delegation_chain_entry->subject_attribute_len];
195   json_t *attr_obj;
196
197   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&delegation_chain_entry->issuer_key);
198   {
199     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200                 "Issuer in delegation malformed\n");
201     return NULL;
202   }
203   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&delegation_chain_entry->subject_key);
204   {
205     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
206                 "Subject in credential malformed\n");
207     GNUNET_free (issuer);
208     return NULL;
209   }
210   attr_obj = json_object ();
211   memcpy (iss_attribute,
212           delegation_chain_entry->issuer_attribute,
213           delegation_chain_entry->issuer_attribute_len);
214   iss_attribute[delegation_chain_entry->issuer_attribute_len] = '\0';
215
216   json_object_set_new (attr_obj, "subject", json_string (subject));
217   json_object_set_new (attr_obj, "issuer", json_string (issuer));
218   json_object_set_new (attr_obj, "issuer_attribute", json_string (iss_attribute));
219
220   if (0 < delegation_chain_entry->subject_attribute_len)
221   {
222     memcpy (sub_attribute,
223             delegation_chain_entry->subject_attribute,
224             delegation_chain_entry->subject_attribute_len);
225     sub_attribute[delegation_chain_entry->subject_attribute_len] = '\0';
226     json_object_set_new (attr_obj, "subject_attribute", json_string (sub_attribute));
227   }
228   GNUNET_free (subject);
229   return attr_obj;
230 }
231
232 /**
233  * Credential to JSON
234  * @param cred the credential
235  * @return the resulting json, NULL if failed
236  */
237 static json_t*
238 credential_to_json (struct GNUNET_CREDENTIAL_Credential *cred)
239 {
240   char *issuer;
241   char *subject;
242   char attribute[cred->issuer_attribute_len + 1];
243   json_t *cred_obj;
244
245   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
246   if (NULL == issuer)
247   {
248     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249                 "Issuer in credential malformed\n");
250     return NULL;
251   }  
252   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
253   if (NULL == subject)
254   {
255     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256                 "Subject in credential malformed\n");
257     GNUNET_free (issuer);
258     return NULL;
259   }
260   memcpy (attribute,
261           cred->issuer_attribute,
262           cred->issuer_attribute_len);
263   attribute[cred->issuer_attribute_len] = '\0';
264   cred_obj = json_object ();
265   json_object_set_new (cred_obj, "issuer", json_string (issuer));
266   json_object_set_new (cred_obj, "subject", json_string (subject));
267   json_object_set_new (cred_obj, "attribute", json_string (attribute));
268   GNUNET_free (issuer);
269   GNUNET_free (subject);
270   return cred_obj;
271 }
272
273 /**
274  * Function called with the result of a Credential lookup.
275  *
276  * @param cls the 'const char *' name that was resolved
277  * @param cd_count number of records returned
278  * @param cd array of @a cd_count records with the results
279  */
280 static void
281 handle_verify_response (void *cls,
282                         unsigned int d_count,
283                         struct GNUNET_CREDENTIAL_Delegation *delegation_chain,
284                         struct GNUNET_CREDENTIAL_Credential *cred)
285 {
286
287   struct VerifyHandle *handle = cls;
288   struct MHD_Response *resp;
289   struct GNUNET_JSONAPI_Document *json_document;
290   struct GNUNET_JSONAPI_Resource *json_resource;
291   json_t *cred_obj;
292   json_t *attr_obj;
293   json_t *result_array;
294   char *result;
295   uint32_t i;
296
297   handle->verify_request = NULL;
298   if (NULL == cred) {
299     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
300                 "Verify failed.\n");
301     handle->response_code = MHD_HTTP_NOT_FOUND;
302     GNUNET_SCHEDULER_add_now (&do_error, handle);
303     return;
304   }
305   json_document = GNUNET_JSONAPI_document_new ();
306   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
307                                                handle->issuer_attr);
308   cred_obj = credential_to_json (cred);
309   result_array = json_array ();
310   for (i = 0; i < d_count; i++)
311   {
312     attr_obj = attribute_delegation_to_json (&delegation_chain[i]);
313     json_array_append (result_array, attr_obj);
314     json_decref (attr_obj);
315   }
316   GNUNET_JSONAPI_resource_add_attr (json_resource,
317                                     GNUNET_REST_JSONAPI_CREDENTIAL,
318                                     cred_obj);
319   GNUNET_JSONAPI_resource_add_attr (json_resource,
320                                     GNUNET_REST_JSONAPI_CREDENTIAL_CHAIN,
321                                     result_array);
322   GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
323   GNUNET_JSONAPI_document_serialize (json_document, &result);
324   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
325               "Result %s\n",
326               result);
327   json_decref (result_array);
328   GNUNET_JSONAPI_document_delete (json_document);
329   resp = GNUNET_REST_create_response (result);
330   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
331   GNUNET_free (result);
332   cleanup_handle (handle);
333 }
334
335
336 static void
337 verify_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
338                   const char* url,
339                   void *cls)
340 {
341   struct VerifyHandle *handle = cls;
342   struct GNUNET_HashCode key;
343   char *tmp;
344   char *entity_attr;
345
346   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347               "Connecting...\n");
348   handle->credential = GNUNET_CREDENTIAL_connect (cfg);
349   handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
350                                                        &do_error, handle);
351   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352               "Connected\n");
353   if (NULL == handle->credential)
354   {
355     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
356                 "Connecting to CREDENTIAL failed\n");
357     GNUNET_SCHEDULER_add_now (&do_error, handle);
358     return;
359   }
360   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
361                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
362                       &key);
363   if ( GNUNET_NO ==
364        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
365                                                &key) )
366   {
367     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
368                 "Missing issuer attribute\n");
369     GNUNET_SCHEDULER_add_now (&do_error, handle); 
370     return;
371   }
372   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
373                                            &key);
374   entity_attr = GNUNET_strdup (tmp);
375   tmp = strtok(entity_attr, ".");
376   if (NULL == tmp)
377   {
378     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
379                 "Malformed issuer or attribute\n");
380     GNUNET_free (entity_attr);
381     GNUNET_SCHEDULER_add_now (&do_error, handle);
382     return;
383   }
384   if (GNUNET_OK != 
385       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
386                                                   strlen (tmp),
387                                                   &handle->issuer_key))
388   {
389     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
390                 "Malformed issuer key\n");
391     GNUNET_free (entity_attr);
392     GNUNET_SCHEDULER_add_now (&do_error, handle);
393     return;
394   }
395   tmp = strtok (NULL, "."); //Issuer attribute
396   if (NULL == tmp)
397   {
398     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
399                 "Malformed attribute\n");
400     GNUNET_free (entity_attr);
401     GNUNET_SCHEDULER_add_now (&do_error, handle);
402     return;
403   }
404   handle->issuer_attr = GNUNET_strdup (tmp);
405   GNUNET_free (entity_attr);
406
407   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR,
408                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR),
409                       &key);
410   if ( GNUNET_NO ==
411        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
412                                                &key) )
413   {
414     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415                 "Missing subject or attribute\n");
416     GNUNET_free (entity_attr);
417     GNUNET_SCHEDULER_add_now (&do_error, handle);
418     return;
419   }
420   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
421                                            &key);
422   entity_attr = GNUNET_strdup (tmp);
423   tmp = strtok(entity_attr, ".");
424   if (NULL == tmp)
425   {
426     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
427                 "Malformed subject\n");
428     GNUNET_free (entity_attr);
429     GNUNET_SCHEDULER_add_now (&do_error, handle); 
430     return;
431   }
432   if (GNUNET_OK !=
433       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
434                                                   strlen (tmp),
435                                                   &handle->subject_key)) {
436     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437                 "Malformed subject key\n");
438     GNUNET_free (entity_attr);
439     GNUNET_SCHEDULER_add_now (&do_error, handle);
440     return;
441   }
442   tmp = strtok (NULL, ".");
443   if (NULL == tmp)
444   {
445     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
446                 "Malformed subject attribute\n");
447     GNUNET_free (entity_attr);
448     GNUNET_SCHEDULER_add_now (&do_error, handle); 
449     return;
450   }
451   handle->subject_attr = GNUNET_strdup (tmp);
452   GNUNET_free (entity_attr);
453
454   handle->verify_request = GNUNET_CREDENTIAL_verify (handle->credential,
455                                                      &handle->issuer_key,
456                                                      handle->issuer_attr,
457                                                      &handle->subject_key,
458                                                      handle->subject_attr,
459                                                      &handle_verify_response,
460                                                      handle);
461
462 }
463
464 /**
465  * Handle rest request
466  *
467  * @param handle the lookup handle
468  */
469 static void
470 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
471               const char* url,
472               void *cls)
473 {
474   struct MHD_Response *resp;
475   struct VerifyHandle *handle = cls;
476
477   //For GNS, independent of path return all options
478   resp = GNUNET_REST_create_response (NULL);
479   MHD_add_response_header (resp,
480                            "Access-Control-Allow-Methods",
481                            MHD_HTTP_METHOD_GET);
482   handle->proc (handle->proc_cls,
483                 resp,
484                 MHD_HTTP_OK);
485   cleanup_handle (handle);
486 }
487
488
489 /**
490  * Function processing the REST call
491  *
492  * @param method HTTP method
493  * @param url URL of the HTTP request
494  * @param data body of the HTTP request (optional)
495  * @param data_size length of the body
496  * @param proc callback function for the result
497  * @param proc_cls closure for callback function
498  * @return GNUNET_OK if request accepted
499  */
500 static void
501 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
502                                 GNUNET_REST_ResultProcessor proc,
503                                 void *proc_cls)
504 {
505   struct VerifyHandle *handle = GNUNET_new (struct VerifyHandle);
506   struct GNUNET_REST_RequestHandlerError err;
507
508   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
509   handle->proc_cls = proc_cls;
510   handle->proc = proc;
511   handle->rest_handle = conndata_handle;
512
513   static const struct GNUNET_REST_RequestHandler handlers[] = {
514     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL, &verify_cred_cont},
515     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont},
516     GNUNET_REST_HANDLER_END
517   };
518
519   if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
520                                                   handlers,
521                                                   &err,
522                                                   handle))
523   {
524     handle->response_code = err.error_code;
525     GNUNET_SCHEDULER_add_now (&do_error, handle);
526   }
527 }
528
529
530 /**
531  * Entry point for the plugin.
532  *
533  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
534  * @return NULL on error, otherwise the plugin context
535  */
536 void *
537 libgnunet_plugin_rest_credential_init (void *cls)
538 {
539   static struct Plugin plugin;
540   cfg = cls;
541   struct GNUNET_REST_Plugin *api;
542
543   if (NULL != plugin.cfg)
544     return NULL;                /* can only initialize once! */
545   memset (&plugin, 0, sizeof (struct Plugin));
546   plugin.cfg = cfg;
547   api = GNUNET_new (struct GNUNET_REST_Plugin);
548   api->cls = &plugin;
549   api->name = GNUNET_REST_API_NS_CREDENTIAL;
550   api->process_request = &rest_credential_process_request;
551   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
552               _("GNS REST API initialized\n"));
553   return api;
554 }
555
556
557 /**
558  * Exit point from the plugin.
559  *
560  * @param cls the plugin context (as returned by "init")
561  * @return always NULL
562  */
563 void *
564 libgnunet_plugin_rest_credential_done (void *cls)
565 {
566   struct GNUNET_REST_Plugin *api = cls;
567   struct Plugin *plugin = api->cls;
568
569   plugin->cfg = NULL;
570   GNUNET_free (api);
571   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572               "GNS REST plugin is finished\n");
573   return NULL;
574 }
575
576 /* end of plugin_rest_gns.c */