-fixes, prettify
[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   {
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                         unsigned int c_count,
277                         struct GNUNET_CREDENTIAL_Credential *cred)
278 {
279
280   struct VerifyHandle *handle = cls;
281   struct MHD_Response *resp;
282   struct GNUNET_JSONAPI_Document *json_document;
283   struct GNUNET_JSONAPI_Resource *json_resource;
284   json_t *cred_obj;
285   json_t *attr_obj;
286   json_t *cred_array;
287   json_t *attr_array;
288   char *result;
289   uint32_t i;
290
291   handle->verify_request = NULL;
292   if (NULL == cred) {
293     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
294                 "Verify failed.\n");
295     handle->response_code = MHD_HTTP_NOT_FOUND;
296     GNUNET_SCHEDULER_add_now (&do_error, handle);
297     return;
298   }
299   json_document = GNUNET_JSONAPI_document_new ();
300   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_CREDENTIAL_TYPEINFO,
301                                                handle->issuer_attr);
302   cred_obj = credential_to_json (cred);
303   attr_array = json_array ();
304   for (i = 0; i < d_count; i++)
305   {
306     attr_obj = attribute_delegation_to_json (&delegation_chain[i]);
307     json_array_append (attr_array, attr_obj);
308     json_decref (attr_obj);
309   }
310   cred_array = json_array ();
311   for (i=0;i<c_count;i++)
312   {
313     cred_obj = credential_to_json (&cred[i]);
314     json_array_append (cred_array, cred_obj);
315     json_decref (cred_obj);
316   }
317   GNUNET_JSONAPI_resource_add_attr (json_resource,
318                                     GNUNET_REST_JSONAPI_CREDENTIAL,
319                                     cred_array);
320   GNUNET_JSONAPI_resource_add_attr (json_resource,
321                                     GNUNET_REST_JSONAPI_DELEGATIONS,
322                                     attr_array);
323   GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
324   GNUNET_JSONAPI_document_serialize (json_document, &result);
325   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326               "Result %s\n",
327               result);
328   json_decref (attr_array);
329   json_decref (cred_array);
330   GNUNET_JSONAPI_document_delete (json_document);
331   resp = GNUNET_REST_create_response (result);
332   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
333   GNUNET_free (result);
334   cleanup_handle (handle);
335 }
336
337
338 static void
339 verify_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
340                   const char* url,
341                   void *cls)
342 {
343   struct VerifyHandle *handle = cls;
344   struct GNUNET_HashCode key;
345   char *tmp;
346   char *entity_attr;
347
348   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349               "Connecting...\n");
350   handle->credential = GNUNET_CREDENTIAL_connect (cfg);
351   handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
352                                                        &do_error, handle);
353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354               "Connected\n");
355   if (NULL == handle->credential)
356   {
357     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
358                 "Connecting to CREDENTIAL failed\n");
359     GNUNET_SCHEDULER_add_now (&do_error, handle);
360     return;
361   }
362   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
363                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
364                       &key);
365   if ( GNUNET_NO ==
366        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
367                                                &key) )
368   {
369     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370                 "Missing issuer attribute\n");
371     GNUNET_SCHEDULER_add_now (&do_error, handle); 
372     return;
373   }
374   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
375                                            &key);
376   entity_attr = GNUNET_strdup (tmp);
377   tmp = strtok(entity_attr, ".");
378   if (NULL == tmp)
379   {
380     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
381                 "Malformed issuer or attribute\n");
382     GNUNET_free (entity_attr);
383     GNUNET_SCHEDULER_add_now (&do_error, handle);
384     return;
385   }
386   if (GNUNET_OK != 
387       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
388                                                   strlen (tmp),
389                                                   &handle->issuer_key))
390   {
391     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
392                 "Malformed issuer key\n");
393     GNUNET_free (entity_attr);
394     GNUNET_SCHEDULER_add_now (&do_error, handle);
395     return;
396   }
397   tmp = strtok (NULL, "."); //Issuer attribute
398   if (NULL == tmp)
399   {
400     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
401                 "Malformed attribute\n");
402     GNUNET_free (entity_attr);
403     GNUNET_SCHEDULER_add_now (&do_error, handle);
404     return;
405   }
406   handle->issuer_attr = GNUNET_strdup (tmp);
407   GNUNET_free (entity_attr);
408
409   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR,
410                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR),
411                       &key);
412   if ( GNUNET_NO ==
413        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
414                                                &key) )
415   {
416     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
417                 "Missing subject or attribute\n");
418     GNUNET_free (entity_attr);
419     GNUNET_SCHEDULER_add_now (&do_error, handle);
420     return;
421   }
422   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
423                                            &key);
424   entity_attr = GNUNET_strdup (tmp);
425   tmp = strtok(entity_attr, ".");
426   if (NULL == tmp)
427   {
428     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429                 "Malformed subject\n");
430     GNUNET_free (entity_attr);
431     GNUNET_SCHEDULER_add_now (&do_error, handle); 
432     return;
433   }
434   if (GNUNET_OK !=
435       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
436                                                   strlen (tmp),
437                                                   &handle->subject_key)) {
438     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
439                 "Malformed subject key\n");
440     GNUNET_free (entity_attr);
441     GNUNET_SCHEDULER_add_now (&do_error, handle);
442     return;
443   }
444   tmp = strtok (NULL, ".");
445   if (NULL == tmp)
446   {
447     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
448                 "Malformed subject attribute\n");
449     GNUNET_free (entity_attr);
450     GNUNET_SCHEDULER_add_now (&do_error, handle); 
451     return;
452   }
453   handle->subject_attr = GNUNET_strdup (tmp);
454   GNUNET_free (entity_attr);
455
456   handle->verify_request = GNUNET_CREDENTIAL_verify (handle->credential,
457                                                      &handle->issuer_key,
458                                                      handle->issuer_attr,
459                                                      &handle->subject_key,
460                                                      handle->subject_attr,
461                                                      &handle_verify_response,
462                                                      handle);
463
464 }
465
466 /**
467  * Handle rest request
468  *
469  * @param handle the lookup handle
470  */
471 static void
472 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
473               const char* url,
474               void *cls)
475 {
476   struct MHD_Response *resp;
477   struct VerifyHandle *handle = cls;
478
479   //For GNS, independent of path return all options
480   resp = GNUNET_REST_create_response (NULL);
481   MHD_add_response_header (resp,
482                            "Access-Control-Allow-Methods",
483                            MHD_HTTP_METHOD_GET);
484   handle->proc (handle->proc_cls,
485                 resp,
486                 MHD_HTTP_OK);
487   cleanup_handle (handle);
488 }
489
490
491 /**
492  * Function processing the REST call
493  *
494  * @param method HTTP method
495  * @param url URL of the HTTP request
496  * @param data body of the HTTP request (optional)
497  * @param data_size length of the body
498  * @param proc callback function for the result
499  * @param proc_cls closure for callback function
500  * @return GNUNET_OK if request accepted
501  */
502 static void
503 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
504                                 GNUNET_REST_ResultProcessor proc,
505                                 void *proc_cls)
506 {
507   struct VerifyHandle *handle = GNUNET_new (struct VerifyHandle);
508   struct GNUNET_REST_RequestHandlerError err;
509
510   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
511   handle->proc_cls = proc_cls;
512   handle->proc = proc;
513   handle->rest_handle = conndata_handle;
514
515   static const struct GNUNET_REST_RequestHandler handlers[] = {
516     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL, &verify_cred_cont},
517     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont},
518     GNUNET_REST_HANDLER_END
519   };
520
521   if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
522                                                   handlers,
523                                                   &err,
524                                                   handle))
525   {
526     handle->response_code = err.error_code;
527     GNUNET_SCHEDULER_add_now (&do_error, handle);
528   }
529 }
530
531
532 /**
533  * Entry point for the plugin.
534  *
535  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
536  * @return NULL on error, otherwise the plugin context
537  */
538 void *
539 libgnunet_plugin_rest_credential_init (void *cls)
540 {
541   static struct Plugin plugin;
542   cfg = cls;
543   struct GNUNET_REST_Plugin *api;
544
545   if (NULL != plugin.cfg)
546     return NULL;                /* can only initialize once! */
547   memset (&plugin, 0, sizeof (struct Plugin));
548   plugin.cfg = cfg;
549   api = GNUNET_new (struct GNUNET_REST_Plugin);
550   api->cls = &plugin;
551   api->name = GNUNET_REST_API_NS_CREDENTIAL;
552   api->process_request = &rest_credential_process_request;
553   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
554               _("GNS REST API initialized\n"));
555   return api;
556 }
557
558
559 /**
560  * Exit point from the plugin.
561  *
562  * @param cls the plugin context (as returned by "init")
563  * @return always NULL
564  */
565 void *
566 libgnunet_plugin_rest_credential_done (void *cls)
567 {
568   struct GNUNET_REST_Plugin *api = cls;
569   struct Plugin *plugin = api->cls;
570
571   plugin->cfg = NULL;
572   GNUNET_free (api);
573   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574               "GNS REST plugin is finished\n");
575   return NULL;
576 }
577
578 /* end of plugin_rest_gns.c */