-fix gns mq; fix 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   json_t *attr_obj;
194
195   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&delegation_chain_entry->issuer_key);
196   {
197     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
198                 "Issuer in delegation malformed\n");
199     return NULL;
200   }
201   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&delegation_chain_entry->subject_key);
202   {
203     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
204                 "Subject in credential malformed\n");
205     GNUNET_free (issuer);
206     return NULL;
207   }
208   attr_obj = json_object ();
209
210   json_object_set_new (attr_obj, "subject", json_string (subject));
211   json_object_set_new (attr_obj, "issuer", json_string (issuer));
212   json_object_set_new (attr_obj, "issuer_attribute",
213                        json_string (delegation_chain_entry->issuer_attribute));
214
215   if (0 < delegation_chain_entry->subject_attribute_len)
216   {
217     json_object_set_new (attr_obj, "subject_attribute",
218                          json_string (delegation_chain_entry->subject_attribute));
219   }
220   GNUNET_free (subject);
221   return attr_obj;
222 }
223
224 /**
225  * Credential to JSON
226  * @param cred the credential
227  * @return the resulting json, NULL if failed
228  */
229 static json_t*
230 credential_to_json (struct GNUNET_CREDENTIAL_Credential *cred)
231 {
232   char *issuer;
233   char *subject;
234   char attribute[cred->issuer_attribute_len + 1];
235   json_t *cred_obj;
236
237   issuer = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->issuer_key);
238   if (NULL == issuer)
239   {
240     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241                 "Issuer in credential malformed\n");
242     return NULL;
243   }  
244   subject = GNUNET_CRYPTO_ecdsa_public_key_to_string (&cred->subject_key);
245   if (NULL == subject)
246   {
247     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248                 "Subject in credential malformed\n");
249     GNUNET_free (issuer);
250     return NULL;
251   }
252   memcpy (attribute,
253           cred->issuer_attribute,
254           cred->issuer_attribute_len);
255   attribute[cred->issuer_attribute_len] = '\0';
256   cred_obj = json_object ();
257   json_object_set_new (cred_obj, "issuer", json_string (issuer));
258   json_object_set_new (cred_obj, "subject", json_string (subject));
259   json_object_set_new (cred_obj, "attribute", json_string (attribute));
260   GNUNET_free (issuer);
261   GNUNET_free (subject);
262   return cred_obj;
263 }
264
265 /**
266  * Function called with the result of a Credential lookup.
267  *
268  * @param cls the 'const char *' name that was resolved
269  * @param cd_count number of records returned
270  * @param cd array of @a cd_count records with the results
271  */
272 static void
273 handle_verify_response (void *cls,
274                         unsigned int d_count,
275                         struct GNUNET_CREDENTIAL_Delegation *delegation_chain,
276                         struct GNUNET_CREDENTIAL_Credential *cred)
277 {
278
279   struct VerifyHandle *handle = cls;
280   struct MHD_Response *resp;
281   struct GNUNET_JSONAPI_Document *json_document;
282   struct GNUNET_JSONAPI_Resource *json_resource;
283   json_t *cred_obj;
284   json_t *attr_obj;
285   json_t *result_array;
286   char *result;
287   uint32_t i;
288
289   handle->verify_request = NULL;
290   if (NULL == cred) {
291     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
292                 "Verify failed.\n");
293     handle->response_code = MHD_HTTP_NOT_FOUND;
294     GNUNET_SCHEDULER_add_now (&do_error, handle);
295     return;
296   }
297   json_document = GNUNET_JSONAPI_document_new ();
298   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
299                                                handle->issuer_attr);
300   cred_obj = credential_to_json (cred);
301   result_array = json_array ();
302   for (i = 0; i < d_count; i++)
303   {
304     attr_obj = attribute_delegation_to_json (&delegation_chain[i]);
305     json_array_append (result_array, attr_obj);
306     json_decref (attr_obj);
307   }
308   GNUNET_JSONAPI_resource_add_attr (json_resource,
309                                     GNUNET_REST_JSONAPI_CREDENTIAL,
310                                     cred_obj);
311   GNUNET_JSONAPI_resource_add_attr (json_resource,
312                                     GNUNET_REST_JSONAPI_CREDENTIAL_CHAIN,
313                                     result_array);
314   GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
315   GNUNET_JSONAPI_document_serialize (json_document, &result);
316   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317               "Result %s\n",
318               result);
319   json_decref (result_array);
320   GNUNET_JSONAPI_document_delete (json_document);
321   resp = GNUNET_REST_create_response (result);
322   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
323   GNUNET_free (result);
324   cleanup_handle (handle);
325 }
326
327
328 static void
329 verify_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
330                   const char* url,
331                   void *cls)
332 {
333   struct VerifyHandle *handle = cls;
334   struct GNUNET_HashCode key;
335   char *tmp;
336   char *entity_attr;
337
338   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339               "Connecting...\n");
340   handle->credential = GNUNET_CREDENTIAL_connect (cfg);
341   handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
342                                                        &do_error, handle);
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344               "Connected\n");
345   if (NULL == handle->credential)
346   {
347     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
348                 "Connecting to CREDENTIAL failed\n");
349     GNUNET_SCHEDULER_add_now (&do_error, handle);
350     return;
351   }
352   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
353                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
354                       &key);
355   if ( GNUNET_NO ==
356        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
357                                                &key) )
358   {
359     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360                 "Missing issuer attribute\n");
361     GNUNET_SCHEDULER_add_now (&do_error, handle); 
362     return;
363   }
364   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
365                                            &key);
366   entity_attr = GNUNET_strdup (tmp);
367   tmp = strtok(entity_attr, ".");
368   if (NULL == tmp)
369   {
370     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371                 "Malformed issuer or attribute\n");
372     GNUNET_free (entity_attr);
373     GNUNET_SCHEDULER_add_now (&do_error, handle);
374     return;
375   }
376   if (GNUNET_OK != 
377       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
378                                                   strlen (tmp),
379                                                   &handle->issuer_key))
380   {
381     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382                 "Malformed issuer key\n");
383     GNUNET_free (entity_attr);
384     GNUNET_SCHEDULER_add_now (&do_error, handle);
385     return;
386   }
387   tmp = strtok (NULL, "."); //Issuer attribute
388   if (NULL == tmp)
389   {
390     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
391                 "Malformed attribute\n");
392     GNUNET_free (entity_attr);
393     GNUNET_SCHEDULER_add_now (&do_error, handle);
394     return;
395   }
396   handle->issuer_attr = GNUNET_strdup (tmp);
397   GNUNET_free (entity_attr);
398
399   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR,
400                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR),
401                       &key);
402   if ( GNUNET_NO ==
403        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
404                                                &key) )
405   {
406     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
407                 "Missing subject or attribute\n");
408     GNUNET_free (entity_attr);
409     GNUNET_SCHEDULER_add_now (&do_error, handle);
410     return;
411   }
412   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
413                                            &key);
414   entity_attr = GNUNET_strdup (tmp);
415   tmp = strtok(entity_attr, ".");
416   if (NULL == tmp)
417   {
418     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
419                 "Malformed subject\n");
420     GNUNET_free (entity_attr);
421     GNUNET_SCHEDULER_add_now (&do_error, handle); 
422     return;
423   }
424   if (GNUNET_OK !=
425       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
426                                                   strlen (tmp),
427                                                   &handle->subject_key)) {
428     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429                 "Malformed subject key\n");
430     GNUNET_free (entity_attr);
431     GNUNET_SCHEDULER_add_now (&do_error, handle);
432     return;
433   }
434   tmp = strtok (NULL, ".");
435   if (NULL == tmp)
436   {
437     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
438                 "Malformed subject attribute\n");
439     GNUNET_free (entity_attr);
440     GNUNET_SCHEDULER_add_now (&do_error, handle); 
441     return;
442   }
443   handle->subject_attr = GNUNET_strdup (tmp);
444   GNUNET_free (entity_attr);
445
446   handle->verify_request = GNUNET_CREDENTIAL_verify (handle->credential,
447                                                      &handle->issuer_key,
448                                                      handle->issuer_attr,
449                                                      &handle->subject_key,
450                                                      handle->subject_attr,
451                                                      &handle_verify_response,
452                                                      handle);
453
454 }
455
456 /**
457  * Handle rest request
458  *
459  * @param handle the lookup handle
460  */
461 static void
462 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
463               const char* url,
464               void *cls)
465 {
466   struct MHD_Response *resp;
467   struct VerifyHandle *handle = cls;
468
469   //For GNS, independent of path return all options
470   resp = GNUNET_REST_create_response (NULL);
471   MHD_add_response_header (resp,
472                            "Access-Control-Allow-Methods",
473                            MHD_HTTP_METHOD_GET);
474   handle->proc (handle->proc_cls,
475                 resp,
476                 MHD_HTTP_OK);
477   cleanup_handle (handle);
478 }
479
480
481 /**
482  * Function processing the REST call
483  *
484  * @param method HTTP method
485  * @param url URL of the HTTP request
486  * @param data body of the HTTP request (optional)
487  * @param data_size length of the body
488  * @param proc callback function for the result
489  * @param proc_cls closure for callback function
490  * @return GNUNET_OK if request accepted
491  */
492 static void
493 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
494                                 GNUNET_REST_ResultProcessor proc,
495                                 void *proc_cls)
496 {
497   struct VerifyHandle *handle = GNUNET_new (struct VerifyHandle);
498   struct GNUNET_REST_RequestHandlerError err;
499
500   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
501   handle->proc_cls = proc_cls;
502   handle->proc = proc;
503   handle->rest_handle = conndata_handle;
504
505   static const struct GNUNET_REST_RequestHandler handlers[] = {
506     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL, &verify_cred_cont},
507     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont},
508     GNUNET_REST_HANDLER_END
509   };
510
511   if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
512                                                   handlers,
513                                                   &err,
514                                                   handle))
515   {
516     handle->response_code = err.error_code;
517     GNUNET_SCHEDULER_add_now (&do_error, handle);
518   }
519 }
520
521
522 /**
523  * Entry point for the plugin.
524  *
525  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
526  * @return NULL on error, otherwise the plugin context
527  */
528 void *
529 libgnunet_plugin_rest_credential_init (void *cls)
530 {
531   static struct Plugin plugin;
532   cfg = cls;
533   struct GNUNET_REST_Plugin *api;
534
535   if (NULL != plugin.cfg)
536     return NULL;                /* can only initialize once! */
537   memset (&plugin, 0, sizeof (struct Plugin));
538   plugin.cfg = cfg;
539   api = GNUNET_new (struct GNUNET_REST_Plugin);
540   api->cls = &plugin;
541   api->name = GNUNET_REST_API_NS_CREDENTIAL;
542   api->process_request = &rest_credential_process_request;
543   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
544               _("GNS REST API initialized\n"));
545   return api;
546 }
547
548
549 /**
550  * Exit point from the plugin.
551  *
552  * @param cls the plugin context (as returned by "init")
553  * @return always NULL
554  */
555 void *
556 libgnunet_plugin_rest_credential_done (void *cls)
557 {
558   struct GNUNET_REST_Plugin *api = cls;
559   struct Plugin *plugin = api->cls;
560
561   plugin->cfg = NULL;
562   GNUNET_free (api);
563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564               "GNS REST plugin is finished\n");
565   return NULL;
566 }
567
568 /* end of plugin_rest_gns.c */