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