- Add rest plugin for credential
[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_ISSUER_ATTR "attribute"
41
42 #define GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR "credential"
43
44 /**
45  * @brief struct returned by the initialization function of the plugin
46  */
47 struct Plugin
48 {
49   const struct GNUNET_CONFIGURATION_Handle *cfg;
50 };
51
52 const struct GNUNET_CONFIGURATION_Handle *cfg;
53
54 struct VerifyHandle
55 {
56   /**
57    * Handle to Credential service.
58    */
59   struct GNUNET_CREDENTIAL_Handle *credential;
60
61   /**
62    * Handle to lookup request
63    */
64   struct GNUNET_CREDENTIAL_Request *verify_request;
65
66   /**
67    * Handle to rest request
68    */
69   struct GNUNET_REST_RequestHandle *rest_handle;
70
71   /**
72    * ID of a task associated with the resolution process.
73    */
74   struct GNUNET_SCHEDULER_Task * timeout_task;
75
76   /**
77    * The root of the received JSON or NULL
78    */
79   json_t *json_root;
80
81   /**
82    * The plugin result processor
83    */
84   GNUNET_REST_ResultProcessor proc;
85
86   /**
87    * The closure of the result processor
88    */
89   void *proc_cls;
90
91   /**
92    * The issuer attribute to verify
93    */
94   char *issuer_attr;
95
96   /**
97    * The subject attribute
98    */
99   char *subject_attr;
100
101   /**
102    * The public key of the issuer
103    */
104   struct GNUNET_CRYPTO_EcdsaPublicKey issuer_key;
105
106   /**
107    * The public key of the subject
108    */
109   struct GNUNET_CRYPTO_EcdsaPublicKey subject_key;
110
111   /**
112    * HTTP response code
113    */
114   int response_code;
115
116   /**
117    * Timeout
118    */
119   struct GNUNET_TIME_Relative timeout;
120
121 };
122
123
124 /**
125  * Cleanup lookup handle.
126  *
127  * @param handle Handle to clean up
128  */
129 static void
130 cleanup_handle (struct VerifyHandle *handle)
131 {
132   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
133               "Cleaning up\n");
134   if (NULL != handle->json_root)
135     json_decref (handle->json_root);
136
137   if (NULL != handle->issuer_attr)
138     GNUNET_free (handle->issuer_attr);
139   if (NULL != handle->subject_attr)
140     GNUNET_free (handle->subject_attr);
141   if (NULL != handle->verify_request)
142   {
143     GNUNET_CREDENTIAL_verify_cancel (handle->verify_request);
144     handle->verify_request = NULL;
145   }
146   if (NULL != handle->credential)
147   {
148     GNUNET_CREDENTIAL_disconnect (handle->credential);
149     handle->credential = NULL;
150   }
151
152   if (NULL != handle->timeout_task)
153   {
154     GNUNET_SCHEDULER_cancel (handle->timeout_task);
155   }
156   GNUNET_free (handle);
157 }
158
159
160 /**
161  * Task run on shutdown.  Cleans up everything.
162  *
163  * @param cls unused
164  * @param tc scheduler context
165  */
166 static void
167 do_error (void *cls)
168 {
169   struct VerifyHandle *handle = cls;
170   struct MHD_Response *resp;
171
172   resp = GNUNET_REST_create_response (NULL);
173   handle->proc (handle->proc_cls, resp, handle->response_code);
174   cleanup_handle (handle);
175 }
176
177
178 static void
179 verify_cred_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
180                   const char* url,
181                   void *cls)
182 {
183   struct VerifyHandle *handle = cls;
184   struct GNUNET_HashCode key;
185   char *tmp;
186   char *entity_attr;
187
188   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
189               "Connecting...\n");
190   handle->credential = GNUNET_CREDENTIAL_connect (cfg);
191   handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
192                                                        &do_error, handle);
193   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
194               "Connected\n");
195   if (NULL == handle->credential)
196   {
197     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
198                 "Connecting to CREDENTIAL failed\n");
199     GNUNET_SCHEDULER_add_now (&do_error, handle);
200     return;
201   }
202   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR,
203                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_ISSUER_ATTR),
204                       &key);
205   if ( GNUNET_NO ==
206        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
207                                                &key) )
208   {
209     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210                 "Missing issuer attribute\n");
211     GNUNET_SCHEDULER_add_now (&do_error, handle); 
212     return;
213   }
214   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
215                                            &key);
216   entity_attr = GNUNET_strdup (tmp);
217   tmp = strtok(entity_attr, ".");
218   if (NULL == tmp)
219   {
220     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
221                 "Malformed issuer or attribute\n");
222     GNUNET_free (entity_attr);
223     GNUNET_SCHEDULER_add_now (&do_error, handle);
224     return;
225   }
226   if (GNUNET_OK != 
227       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
228                                                   strlen (tmp),
229                                                   &handle->issuer_key))
230   {
231     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
232                 "Malformed issuer key\n");
233     GNUNET_free (entity_attr);
234     GNUNET_SCHEDULER_add_now (&do_error, handle);
235     return;
236   }
237   tmp = strtok (NULL, "."); //Issuer attribute
238   if (NULL == tmp)
239   {
240     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241                 "Malformed attribute\n");
242     GNUNET_free (entity_attr);
243     GNUNET_SCHEDULER_add_now (&do_error, handle);
244     return;
245   }
246   handle->issuer_attr = GNUNET_strdup (tmp);
247   GNUNET_free (entity_attr);
248
249   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR,
250                       strlen (GNUNET_REST_JSONAPI_CREDENTIAL_SUBJECT_ATTR),
251                       &key);
252   if ( GNUNET_NO ==
253        GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
254                                                &key) )
255   {
256     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
257                 "Missing subject or attribute\n");
258     GNUNET_free (entity_attr);
259     GNUNET_SCHEDULER_add_now (&do_error, handle);
260     return;
261   }
262   tmp = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
263                                            &key);
264   entity_attr = GNUNET_strdup (tmp);
265   tmp = strtok(entity_attr, ".");
266   if (NULL == tmp)
267   {
268     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
269                 "Malformed subject\n");
270     GNUNET_free (entity_attr);
271     GNUNET_SCHEDULER_add_now (&do_error, handle); 
272     return;
273   }
274   if (GNUNET_OK !=
275       GNUNET_CRYPTO_ecdsa_public_key_from_string (tmp,
276                                                   strlen (tmp),
277                                                   &handle->subject_key)) {
278     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
279                 "Malformed subject key\n");
280     GNUNET_free (entity_attr);
281     GNUNET_SCHEDULER_add_now (&do_error, handle);
282     return;
283   }
284   tmp = strtok (NULL, ".");
285   if (NULL == tmp)
286   {
287     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
288                 "Malformed subject attribute\n");
289     GNUNET_free (entity_attr);
290     GNUNET_SCHEDULER_add_now (&do_error, handle); 
291     return;
292   }
293   handle->subject_attr = GNUNET_strdup (tmp);
294   GNUNET_free (entity_attr);
295   
296   handle->verify_request = GNUNET_CREDENTIAL_verify (handle->credential,
297                                                      &handle->issuer_key,
298                                                      handle->issuer_attr,
299                                                      &handle->subject_key,
300                                                      handle->subject_attr,
301                                                      NULL,
302                                                      NULL);
303
304 }
305
306 /**
307  * Handle rest request
308  *
309  * @param handle the lookup handle
310  */
311 static void
312 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
313               const char* url,
314               void *cls)
315 {
316   struct MHD_Response *resp;
317   struct VerifyHandle *handle = cls;
318
319   //For GNS, independent of path return all options
320   resp = GNUNET_REST_create_response (NULL);
321   MHD_add_response_header (resp,
322                            "Access-Control-Allow-Methods",
323                            MHD_HTTP_METHOD_GET);
324   handle->proc (handle->proc_cls,
325                 resp,
326                 MHD_HTTP_OK);
327   cleanup_handle (handle);
328 }
329
330
331 /**
332  * Function processing the REST call
333  *
334  * @param method HTTP method
335  * @param url URL of the HTTP request
336  * @param data body of the HTTP request (optional)
337  * @param data_size length of the body
338  * @param proc callback function for the result
339  * @param proc_cls closure for callback function
340  * @return GNUNET_OK if request accepted
341  */
342 static void
343 rest_credential_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
344                          GNUNET_REST_ResultProcessor proc,
345                          void *proc_cls)
346 {
347   struct VerifyHandle *handle = GNUNET_new (struct VerifyHandle);
348   struct GNUNET_REST_RequestHandlerError err;
349
350   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
351   handle->proc_cls = proc_cls;
352   handle->proc = proc;
353   handle->rest_handle = conndata_handle;
354
355   static const struct GNUNET_REST_RequestHandler handlers[] = {
356     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_CREDENTIAL, &verify_cred_cont},
357     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_CREDENTIAL, &options_cont},
358     GNUNET_REST_HANDLER_END
359   };
360
361   if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
362                                                   handlers,
363                                                   &err,
364                                                   handle))
365   {
366     handle->response_code = err.error_code;
367     GNUNET_SCHEDULER_add_now (&do_error, handle);
368   }
369 }
370
371
372 /**
373  * Entry point for the plugin.
374  *
375  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
376  * @return NULL on error, otherwise the plugin context
377  */
378 void *
379 libgnunet_plugin_rest_credential_init (void *cls)
380 {
381   static struct Plugin plugin;
382   cfg = cls;
383   struct GNUNET_REST_Plugin *api;
384
385   if (NULL != plugin.cfg)
386     return NULL;                /* can only initialize once! */
387   memset (&plugin, 0, sizeof (struct Plugin));
388   plugin.cfg = cfg;
389   api = GNUNET_new (struct GNUNET_REST_Plugin);
390   api->cls = &plugin;
391   api->name = GNUNET_REST_API_NS_CREDENTIAL;
392   api->process_request = &rest_credential_process_request;
393   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
394               _("GNS REST API initialized\n"));
395   return api;
396 }
397
398
399 /**
400  * Exit point from the plugin.
401  *
402  * @param cls the plugin context (as returned by "init")
403  * @return always NULL
404  */
405 void *
406 libgnunet_plugin_rest_credential_done (void *cls)
407 {
408   struct GNUNET_REST_Plugin *api = cls;
409   struct Plugin *plugin = api->cls;
410
411   plugin->cfg = NULL;
412   GNUNET_free (api);
413   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
414               "GNS REST plugin is finished\n");
415   return NULL;
416 }
417
418 /* end of plugin_rest_gns.c */