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