plugin datastore mysql
[oweals/gnunet.git] / src / identity-provider / identity_provider_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 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 Liceidentity 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 Liceidentity for more details.
14
15      You should have received a copy of the GNU General Public Liceidentity
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 /**
22  * @file identity-provider/identity_provider_api.c
23  * @brief api to interact with the identity provider service
24  * @author Martin Schanzenbach
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_identity_provider_service.h"
31 #include "identity_provider.h"
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "identity-api",__VA_ARGS__)
34
35
36
37 /**
38  * Handle for an operation with the service.
39  */
40 struct GNUNET_IDENTITY_PROVIDER_Operation
41 {
42
43   /**
44    * Main handle.
45    */
46   struct GNUNET_IDENTITY_PROVIDER_Handle *h;
47
48   /**
49    * We keep operations in a DLL.
50    */
51   struct GNUNET_IDENTITY_PROVIDER_Operation *next;
52
53   /**
54    * We keep operations in a DLL.
55    */
56   struct GNUNET_IDENTITY_PROVIDER_Operation *prev;
57
58   /**
59    * Message to send to the service.
60    * Allocated at the end of this struct.
61    */
62   const struct GNUNET_MessageHeader *msg;
63
64   /**
65    * Continuation to invoke with the result of the transmission; @e cb
66    * will be NULL in this case.
67    */
68   GNUNET_IDENTITY_PROVIDER_ExchangeCallback ex_cb;
69
70   /**
71    * Continuation to invoke with the result of the transmission for
72    * 'issue' operations (@e cont will be NULL in this case).
73    */
74   GNUNET_IDENTITY_PROVIDER_IssueCallback iss_cb;
75
76   /**
77    * Closure for @e cont or @e cb.
78    */
79   void *cls;
80
81 };
82
83
84 /**
85  * Handle for the service.
86  */
87 struct GNUNET_IDENTITY_PROVIDER_Handle
88 {
89   /**
90    * Configuration to use.
91    */
92   const struct GNUNET_CONFIGURATION_Handle *cfg;
93
94   /**
95    * Socket (if available).
96    */
97   struct GNUNET_CLIENT_Connection *client;
98
99   /**
100    * Closure for 'cb'.
101    */
102   void *cb_cls;
103
104   /**
105    * Head of active operations.
106    */
107   struct GNUNET_IDENTITY_PROVIDER_Operation *op_head;
108
109   /**
110    * Tail of active operations.
111    */
112   struct GNUNET_IDENTITY_PROVIDER_Operation *op_tail;
113
114   /**
115    * Currently pending transmission request, or NULL for none.
116    */
117   struct GNUNET_CLIENT_TransmitHandle *th;
118
119   /**
120    * Task doing exponential back-off trying to reconnect.
121    */
122   struct GNUNET_SCHEDULER_Task * reconnect_task;
123
124   /**
125    * Time for next connect retry.
126    */
127   struct GNUNET_TIME_Relative reconnect_delay;
128
129   /**
130    * Are we polling for incoming messages right now?
131    */
132   int in_receive;
133
134 };
135
136
137 /**
138  * Try again to connect to the service.
139  *
140  * @param cls handle to the service.
141  */
142 static void
143 reconnect (void *cls);
144
145
146 /**
147  * Reschedule a connect attempt to the service.
148  *
149  * @param h transport service to reconnect
150  */
151 static void
152 reschedule_connect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
153 {
154   GNUNET_assert (h->reconnect_task == NULL);
155
156   if (NULL != h->th)
157   {
158     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
159     h->th = NULL;
160   }
161   if (NULL != h->client)
162   {
163     GNUNET_CLIENT_disconnect (h->client);
164     h->client = NULL;
165   }
166   h->in_receive = GNUNET_NO;
167   LOG (GNUNET_ERROR_TYPE_DEBUG,
168        "Scheduling task to reconnect to identity provider service in %s.\n",
169        GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
170   h->reconnect_task =
171       GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
172   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
173 }
174
175
176 /**
177  * Type of a function to call when we receive a message
178  * from the service.
179  *
180  * @param cls closure
181  * @param msg message received, NULL on timeout or fatal error
182  */
183 static void
184 message_handler (void *cls,
185                  const struct GNUNET_MessageHeader *msg)
186 {
187   struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
188   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
189   struct GNUNET_IDENTITY_PROVIDER_Token token;
190   struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
191   const struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage *irm;
192   const struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage *erm;
193   char *str;
194   char *ticket_str;
195   char *token_str;
196   char *label_str;
197   uint16_t size;
198   uint64_t ticket_nonce;
199
200   if (NULL == msg)
201   {
202     reschedule_connect (h);
203     return;
204   }
205   LOG (GNUNET_ERROR_TYPE_DEBUG,
206        "Received message of type %d from identity provider service\n",
207        ntohs (msg->type));
208   size = ntohs (msg->size);
209   switch (ntohs (msg->type))
210   {
211   case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT:
212     if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
213     {
214       GNUNET_break (0);
215       reschedule_connect (h);
216       return;
217     }
218     irm = (const struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage *) msg;
219     str = GNUNET_strdup ((char *) &irm[1]);
220     if ( (size > sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage)) &&
221          ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage) - 1]) )
222     {
223       GNUNET_free (str);
224       GNUNET_break (0);
225       reschedule_connect (h);
226       return;
227     }
228     if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
229     {
230       GNUNET_free (str);
231       str = NULL;
232     }
233     label_str = strtok (str, ",");
234
235     if (NULL == label_str)
236     {
237       GNUNET_free (str);
238       GNUNET_break (0);
239       reschedule_connect (h);
240       return;
241     }
242     ticket_str = strtok (NULL, ",");
243     if (NULL == ticket_str)
244     {
245       GNUNET_free (str);
246       GNUNET_break (0);
247       reschedule_connect (h);
248       return;
249     }
250     token_str = strtok (NULL, ",");
251     if (NULL == token_str)
252     {
253       GNUNET_free (str);
254       GNUNET_break (0);
255       reschedule_connect (h);
256       return;
257     }
258     op = h->op_head;
259     GNUNET_CONTAINER_DLL_remove (h->op_head,
260                                  h->op_tail,
261                                  op);
262     GNUNET_CLIENT_receive (h->client, &message_handler, h,
263                            GNUNET_TIME_UNIT_FOREVER_REL);
264     ticket.data = ticket_str;
265     token.data = token_str;
266     if (NULL != op->iss_cb)
267       op->iss_cb (op->cls, label_str, &ticket, &token);
268     GNUNET_free (str);
269     GNUNET_free (op);
270     break;
271    case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT:
272     if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
273     {
274       GNUNET_break (0);
275       reschedule_connect (h);
276       return;
277     }
278     erm = (const struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage *) msg;
279     str = (char *) &erm[1];
280     if ( (size > sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage)) &&
281          ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage) - 1]) )
282     {
283       GNUNET_break (0);
284       reschedule_connect (h);
285       return;
286     }
287     if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
288       str = NULL;
289
290     op = h->op_head;
291     GNUNET_CONTAINER_DLL_remove (h->op_head,
292                                  h->op_tail,
293                                  op);
294     GNUNET_CLIENT_receive (h->client, &message_handler, h,
295                            GNUNET_TIME_UNIT_FOREVER_REL);
296     token.data = str;
297     ticket_nonce = ntohl (erm->ticket_nonce);
298     if (NULL != op->ex_cb)
299       op->ex_cb (op->cls, &token, ticket_nonce);
300     GNUNET_free (op);
301     break;
302
303   default:
304     GNUNET_break (0);
305     reschedule_connect (h);
306     return;
307   }
308 }
309
310
311 /**
312  * Schedule transmission of the next message from our queue.
313  *
314  * @param h identity handle
315  */
316 static void
317 transmit_next (struct GNUNET_IDENTITY_PROVIDER_Handle *h);
318
319
320 /**
321  * Transmit next message to service.
322  *
323  * @param cls the `struct GNUNET_IDENTITY_PROVIDER_Handle`.
324  * @param size number of bytes available in @a buf
325  * @param buf where to copy the message
326  * @return number of bytes copied to buf
327  */
328 static size_t
329 send_next_message (void *cls,
330                    size_t size,
331                    void *buf)
332 {
333   struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
334   struct GNUNET_IDENTITY_PROVIDER_Operation *op = h->op_head;
335   size_t ret;
336
337   h->th = NULL;
338   if (NULL == op)
339     return 0;
340   ret = ntohs (op->msg->size);
341   if (ret > size)
342   {
343     reschedule_connect (h);
344     return 0;
345   }
346   LOG (GNUNET_ERROR_TYPE_DEBUG,
347        "Sending message of type %d to identity provider service\n",
348        ntohs (op->msg->type));
349   memcpy (buf, op->msg, ret);
350   if ( (NULL == op->iss_cb) &&
351        (NULL == op->ex_cb) )
352   {
353     GNUNET_CONTAINER_DLL_remove (h->op_head,
354                                  h->op_tail,
355                                  op);
356     GNUNET_free (op);
357     transmit_next (h);
358   }
359   if (GNUNET_NO == h->in_receive)
360   {
361     h->in_receive = GNUNET_YES;
362     GNUNET_CLIENT_receive (h->client,
363                            &message_handler, h,
364                            GNUNET_TIME_UNIT_FOREVER_REL);
365   }
366   return ret;
367 }
368
369
370 /**
371  * Schedule transmission of the next message from our queue.
372  *
373  * @param h identity provider handle
374  */
375 static void
376 transmit_next (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
377 {
378   struct GNUNET_IDENTITY_PROVIDER_Operation *op = h->op_head;
379
380   GNUNET_assert (NULL == h->th);
381   if (NULL == op)
382     return;
383   if (NULL == h->client)
384     return;
385   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
386                                                ntohs (op->msg->size),
387                                                GNUNET_TIME_UNIT_FOREVER_REL,
388                                                GNUNET_NO,
389                                                &send_next_message,
390                                                h);
391 }
392
393
394 /**
395  * Try again to connect to the service.
396  *
397  * @param cls handle to the identity provider service.
398  */
399 static void
400 reconnect (void *cls)
401 {
402   struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
403
404   h->reconnect_task = NULL;
405   LOG (GNUNET_ERROR_TYPE_DEBUG,
406        "Connecting to identity provider service.\n");
407   GNUNET_assert (NULL == h->client);
408   h->client = GNUNET_CLIENT_connect ("identity-provider", h->cfg);
409   GNUNET_assert (NULL != h->client);
410   transmit_next (h);
411   GNUNET_assert (NULL != h->th);
412 }
413
414
415 /**
416  * Connect to the identity provider service.
417  *
418  * @param cfg the configuration to use
419  * @return handle to use
420  */
421 struct GNUNET_IDENTITY_PROVIDER_Handle *
422 GNUNET_IDENTITY_PROVIDER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
423 {
424   struct GNUNET_IDENTITY_PROVIDER_Handle *h;
425
426   h = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Handle);
427   h->cfg = cfg;
428   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
429   h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
430   return h;
431 }
432
433
434 /**
435  * Issue an identity token
436  *
437  * @param id identity service to query
438  * @param service_name for which service is an identity wanted
439  * @param cb function to call with the result (will only be called once)
440  * @param cb_cls closure for @a cb
441  * @return handle to abort the operation
442  */
443 struct GNUNET_IDENTITY_PROVIDER_Operation *
444 GNUNET_IDENTITY_PROVIDER_issue_token (struct GNUNET_IDENTITY_PROVIDER_Handle *id,
445                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *iss_key,
446          const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
447          const char* scopes,
448          struct GNUNET_TIME_Absolute expiration,
449          uint64_t nonce,
450                      GNUNET_IDENTITY_PROVIDER_IssueCallback cb,
451                      void *cb_cls)
452 {
453   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
454   struct GNUNET_IDENTITY_PROVIDER_IssueMessage *im;
455   size_t slen;
456
457   slen = strlen (scopes) + 1;
458   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage))
459   {
460     GNUNET_break (0);
461     return NULL;
462   }
463   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
464                       sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage) +
465                       slen);
466   op->h = id;
467   op->iss_cb = cb;
468   op->cls = cb_cls;
469   im = (struct GNUNET_IDENTITY_PROVIDER_IssueMessage *) &op[1];
470   im->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE);
471   im->header.size = htons (sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage) +
472                             slen);
473   im->iss_key = *iss_key;
474   im->aud_key = *aud_key;
475   im->nonce = htonl (nonce);
476   im->expiration = GNUNET_TIME_absolute_hton (expiration);
477   memcpy (&im[1], scopes, slen);
478   op->msg = &im->header;
479   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
480                                     id->op_tail,
481                                     op);
482   if (NULL == id->th)
483     transmit_next (id);
484   return op;
485 }
486
487
488 /**
489  * Exchange a token ticket for a token
490  *
491  * @param id identity provider service
492  * @param ticket ticket to exchange
493  * @param cont function to call once the operation finished
494  * @param cont_cls closure for @a cont
495  * @return handle to abort the operation
496  */
497 struct GNUNET_IDENTITY_PROVIDER_Operation *
498 GNUNET_IDENTITY_PROVIDER_exchange_ticket (struct GNUNET_IDENTITY_PROVIDER_Handle *id,
499                                           const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
500                                           const struct GNUNET_CRYPTO_EcdsaPrivateKey *aud_privkey,
501                                           GNUNET_IDENTITY_PROVIDER_ExchangeCallback cont,
502                                           void *cont_cls)
503 {
504   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
505   struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *em;
506   size_t slen;
507   char *ticket_str;
508
509   ticket_str = GNUNET_IDENTITY_PROVIDER_ticket_to_string (ticket);
510
511   slen = strlen (ticket_str) + 1;
512   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage))
513   {
514     GNUNET_break (0);
515     return NULL;
516   }
517   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
518                       sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage) +
519                       slen);
520   op->h = id;
521   op->ex_cb = cont;
522   op->cls = cont_cls;
523   em = (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *) &op[1];
524   em->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE);
525   em->header.size = htons (sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage) +
526                            slen);
527   em->aud_privkey = *aud_privkey;
528   memcpy (&em[1], ticket_str, slen);
529   GNUNET_free (ticket_str);
530   op->msg = &em->header;
531   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
532                                     id->op_tail,
533                                     op);
534   if (NULL == id->th)
535     transmit_next (id);
536   return op;
537 }
538
539
540 /**
541  * Cancel an operation. Note that the operation MAY still
542  * be executed; this merely cancels the continuation; if the request
543  * was already transmitted, the service may still choose to complete
544  * the operation.
545  *
546  * @param op operation to cancel
547  */
548 void
549 GNUNET_IDENTITY_PROVIDER_cancel (struct GNUNET_IDENTITY_PROVIDER_Operation *op)
550 {
551   struct GNUNET_IDENTITY_PROVIDER_Handle *h = op->h;
552
553   if ( (h->op_head != op) ||
554        (NULL == h->client) )
555   {
556     /* request not active, can simply remove */
557     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
558                 "Client aborted non-head operation, simply removing it\n");
559     GNUNET_CONTAINER_DLL_remove (h->op_head,
560                                  h->op_tail,
561                                  op);
562     GNUNET_free (op);
563     return;
564   }
565   if (NULL != h->th)
566   {
567     /* request active but not yet with service, can still abort */
568     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
569                 "Client aborted head operation prior to transmission, aborting it\n");
570     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
571     h->th = NULL;
572     GNUNET_CONTAINER_DLL_remove (h->op_head,
573                                  h->op_tail,
574                                  op);
575     GNUNET_free (op);
576     transmit_next (h);
577     return;
578   }
579   /* request active with service, simply ensure continuations are not called */
580   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581               "Client aborted active request, NULLing continuation\n");
582   op->ex_cb = NULL;
583   op->iss_cb = NULL;
584 }
585
586
587 /**
588  * Disconnect from service
589  *
590  * @param h handle to destroy
591  */
592 void
593 GNUNET_IDENTITY_PROVIDER_disconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
594 {
595   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
596
597   GNUNET_assert (NULL != h);
598   if (h->reconnect_task != NULL)
599   {
600     GNUNET_SCHEDULER_cancel (h->reconnect_task);
601     h->reconnect_task = NULL;
602   }
603   if (NULL != h->th)
604   {
605     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
606     h->th = NULL;
607   }
608   while (NULL != (op = h->op_head))
609   {
610     GNUNET_CONTAINER_DLL_remove (h->op_head,
611                                  h->op_tail,
612                                  op);
613     GNUNET_free (op);
614   }
615   if (NULL != h->client)
616   {
617     GNUNET_CLIENT_disconnect (h->client);
618     h->client = NULL;
619   }
620   GNUNET_free (h);
621 }
622
623 /**
624  * Convenience API
625  */
626
627
628 /**
629  * Destroy token
630  *
631  * @param token the token
632  */
633 void
634 GNUNET_IDENTITY_PROVIDER_token_destroy(struct GNUNET_IDENTITY_PROVIDER_Token *token)
635 {
636   GNUNET_assert (NULL != token);
637   if (NULL != token->data)
638     GNUNET_free (token->data);
639   GNUNET_free (token);
640 }
641
642 /**
643  * Returns string representation of token. A JSON-Web-Token.
644  *
645  * @param token the token
646  * @return The JWT (must be freed)
647  */
648 char *
649 GNUNET_IDENTITY_PROVIDER_token_to_string (const struct GNUNET_IDENTITY_PROVIDER_Token *token)
650 {
651   return GNUNET_strdup (token->data);
652 }
653
654 /**
655  * Returns string representation of ticket. Base64-Encoded
656  *
657  * @param ticket the ticket
658  * @return the Base64-Encoded ticket
659  */
660 char *
661 GNUNET_IDENTITY_PROVIDER_ticket_to_string (const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
662 {
663   return GNUNET_strdup (ticket->data);
664 }
665
666 /**
667  * Created a ticket from a string (Base64 encoded ticket)
668  *
669  * @param input Base64 encoded ticket
670  * @param ticket pointer where the ticket is stored
671  * @return GNUNET_OK
672  */
673 int
674 GNUNET_IDENTITY_PROVIDER_string_to_ticket (const char* input,
675                                            struct GNUNET_IDENTITY_PROVIDER_Ticket **ticket)
676 {
677   *ticket = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket));
678   (*ticket)->data = GNUNET_strdup (input);
679   return GNUNET_OK;
680 }
681
682
683 /**
684  * Destroys a ticket
685  *
686  * @param ticket the ticket to destroy
687  */
688 void
689 GNUNET_IDENTITY_PROVIDER_ticket_destroy(struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
690 {
691   GNUNET_assert (NULL != ticket);
692   if (NULL != ticket->data)
693     GNUNET_free (ticket->data);
694   GNUNET_free (ticket);
695 }
696
697
698
699
700
701 /* end of identity_provider_api.c */