-remove debug output
[oweals/gnunet.git] / src / identity-provider / identity_provider_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2016 Christian Grothoff (and other contributing authors)
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  * @param tc scheduler context
142  */
143 static void
144 reconnect (void *cls,
145            const struct GNUNET_SCHEDULER_TaskContext *tc);
146
147
148 /**
149  * Reschedule a connect attempt to the service.
150  *
151  * @param h transport service to reconnect
152  */
153 static void
154 reschedule_connect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
155 {
156   GNUNET_assert (h->reconnect_task == NULL);
157
158   if (NULL != h->th)
159   {
160     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
161     h->th = NULL;
162   }
163   if (NULL != h->client)
164   {
165     GNUNET_CLIENT_disconnect (h->client);
166     h->client = NULL;
167   }
168   h->in_receive = GNUNET_NO;
169   LOG (GNUNET_ERROR_TYPE_DEBUG,
170        "Scheduling task to reconnect to identity provider service in %s.\n",
171        GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
172   h->reconnect_task =
173       GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
174   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
175 }
176
177
178 /**
179  * Type of a function to call when we receive a message
180  * from the service.
181  *
182  * @param cls closure
183  * @param msg message received, NULL on timeout or fatal error
184  */
185 static void
186 message_handler (void *cls,
187                  const struct GNUNET_MessageHeader *msg)
188 {
189   struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
190   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
191   struct GNUNET_IDENTITY_PROVIDER_Token token;
192   struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
193   const struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage *irm;
194   const struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage *erm;
195   char *str;
196   char *ticket_str;
197   char *token_str;
198   char *label_str;
199   uint16_t size;
200
201   if (NULL == msg)
202   {
203     reschedule_connect (h);
204     return;
205   }
206   LOG (GNUNET_ERROR_TYPE_DEBUG,
207        "Received message of type %d from identity provider service\n",
208        ntohs (msg->type));
209   size = ntohs (msg->size);
210   switch (ntohs (msg->type))
211   {
212   case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT:
213     if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
214     {
215       GNUNET_break (0);
216       reschedule_connect (h);
217       return;
218     }
219     irm = (const struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage *) msg;
220     str = GNUNET_strdup ((char *) &irm[1]);
221     if ( (size > sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage)) &&
222          ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage) - 1]) )
223     {
224       GNUNET_free (str);
225       GNUNET_break (0);
226       reschedule_connect (h);
227       return;
228     }
229     if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
230     {
231       GNUNET_free (str);
232       str = NULL;
233     }
234     label_str = strtok (str, ",");
235     
236     if (NULL == label_str)
237     {
238       GNUNET_free (str);
239       GNUNET_break (0);
240       reschedule_connect (h);
241       return;
242     }
243     ticket_str = strtok (NULL, ",");
244     if (NULL == ticket_str)
245     {
246       GNUNET_free (str);
247       GNUNET_break (0);
248       reschedule_connect (h);
249       return;
250     }
251     token_str = strtok (NULL, ",");
252     if (NULL == token_str)
253     {
254       GNUNET_free (str);
255       GNUNET_break (0);
256       reschedule_connect (h);
257       return;
258     }
259     op = h->op_head;
260     GNUNET_CONTAINER_DLL_remove (h->op_head,
261                                  h->op_tail,
262                                  op);
263     GNUNET_CLIENT_receive (h->client, &message_handler, h,
264                            GNUNET_TIME_UNIT_FOREVER_REL);
265     ticket.data = ticket_str;
266     token.data = token_str;
267     if (NULL != op->iss_cb)
268       op->iss_cb (op->cls, label_str, &ticket, &token);
269     GNUNET_free (str);
270     GNUNET_free (op);
271     break;
272    case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT:
273     if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
274     {
275       GNUNET_break (0);
276       reschedule_connect (h);
277       return;
278     }
279     erm = (const struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage *) msg;
280     str = (char *) &erm[1];
281     if ( (size > sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage)) &&
282          ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage) - 1]) )
283     {
284       GNUNET_break (0);
285       reschedule_connect (h);
286       return;
287     }
288     if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
289       str = NULL;
290
291     op = h->op_head;
292     GNUNET_CONTAINER_DLL_remove (h->op_head,
293                                  h->op_tail,
294                                  op);
295     GNUNET_CLIENT_receive (h->client, &message_handler, h,
296                            GNUNET_TIME_UNIT_FOREVER_REL);
297     token.data = str;
298     if (NULL != op->ex_cb)
299       op->ex_cb (op->cls, &token);
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  * @param tc scheduler context
399  */
400 static void
401 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
402 {
403   struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
404
405   h->reconnect_task = NULL;
406   LOG (GNUNET_ERROR_TYPE_DEBUG,
407        "Connecting to identity provider service.\n");
408   GNUNET_assert (NULL == h->client);
409   h->client = GNUNET_CLIENT_connect ("identity-provider", h->cfg);
410   GNUNET_assert (NULL != h->client);
411   transmit_next (h);
412   GNUNET_assert (NULL != h->th);
413 }
414
415
416 /**
417  * Connect to the identity provider service.
418  *
419  * @param cfg the configuration to use
420  * @return handle to use
421  */
422 struct GNUNET_IDENTITY_PROVIDER_Handle *
423 GNUNET_IDENTITY_PROVIDER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
424 {
425   struct GNUNET_IDENTITY_PROVIDER_Handle *h;
426
427   h = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Handle);
428   h->cfg = cfg;
429   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
430   h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
431   return h;
432 }
433
434
435 /**
436  * Issue an identity token
437  *
438  * @param id identity service to query
439  * @param service_name for which service is an identity wanted
440  * @param cb function to call with the result (will only be called once)
441  * @param cb_cls closure for @a cb
442  * @return handle to abort the operation
443  */
444 struct GNUNET_IDENTITY_PROVIDER_Operation *
445 GNUNET_IDENTITY_PROVIDER_issue_token (struct GNUNET_IDENTITY_PROVIDER_Handle *id,
446                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *iss_key,
447          const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
448          const char* scopes,
449          struct GNUNET_TIME_Absolute expiration,
450          uint64_t nonce,
451                      GNUNET_IDENTITY_PROVIDER_IssueCallback cb,
452                      void *cb_cls)
453 {
454   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
455   struct GNUNET_IDENTITY_PROVIDER_IssueMessage *im;
456   size_t slen;
457
458   slen = strlen (scopes) + 1;
459   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage))
460   {
461     GNUNET_break (0);
462     return NULL;
463   }
464   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
465                       sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage) +
466                       slen);
467   op->h = id;
468   op->iss_cb = cb;
469   op->cls = cb_cls;
470   im = (struct GNUNET_IDENTITY_PROVIDER_IssueMessage *) &op[1];
471   im->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE);
472   im->header.size = htons (sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage) +
473                             slen);
474   im->iss_key = *iss_key;
475   im->aud_key = *aud_key;
476   im->nonce = htonl (nonce);
477   im->expiration = GNUNET_TIME_absolute_hton (expiration);
478   memcpy (&im[1], scopes, slen);
479   op->msg = &im->header;
480   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
481                                     id->op_tail,
482                                     op);
483   if (NULL == id->th)
484     transmit_next (id);
485   return op;
486 }
487
488
489 /**
490  * Exchange a token ticket for a token
491  *
492  * @param id identity provider service
493  * @param ticket ticket to exchange
494  * @param cont function to call once the operation finished
495  * @param cont_cls closure for @a cont
496  * @return handle to abort the operation
497  */
498 struct GNUNET_IDENTITY_PROVIDER_Operation *
499 GNUNET_IDENTITY_PROVIDER_exchange_ticket (struct GNUNET_IDENTITY_PROVIDER_Handle *id,
500                                           const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
501                                           const struct GNUNET_CRYPTO_EcdsaPrivateKey *aud_privkey,
502                                           GNUNET_IDENTITY_PROVIDER_ExchangeCallback cont,
503                                           void *cont_cls)
504 {
505   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
506   struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *em;
507   size_t slen;
508   char *ticket_str;
509
510   ticket_str = GNUNET_IDENTITY_PROVIDER_ticket_to_string (ticket);
511
512   slen = strlen (ticket_str) + 1;
513   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage))
514   {
515     GNUNET_break (0);
516     return NULL;
517   }
518   op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
519                       sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage) +
520                       slen);
521   op->h = id;
522   op->ex_cb = cont;
523   op->cls = cont_cls;
524   em = (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *) &op[1];
525   em->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE);
526   em->header.size = htons (sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage) +
527                            slen);
528   em->aud_privkey = *aud_privkey;
529   memcpy (&em[1], ticket_str, slen);
530   GNUNET_free (ticket_str);
531   op->msg = &em->header;
532   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
533                                     id->op_tail,
534                                     op);
535   if (NULL == id->th)
536     transmit_next (id);
537   return op;
538 }
539
540
541 /**
542  * Cancel an operation. Note that the operation MAY still
543  * be executed; this merely cancels the continuation; if the request
544  * was already transmitted, the service may still choose to complete
545  * the operation.
546  *
547  * @param op operation to cancel
548  */
549 void
550 GNUNET_IDENTITY_PROVIDER_cancel (struct GNUNET_IDENTITY_PROVIDER_Operation *op)
551 {
552   struct GNUNET_IDENTITY_PROVIDER_Handle *h = op->h;
553
554   if ( (h->op_head != op) ||
555        (NULL == h->client) )
556   {
557     /* request not active, can simply remove */
558     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559                 "Client aborted non-head operation, simply removing it\n");
560     GNUNET_CONTAINER_DLL_remove (h->op_head,
561                                  h->op_tail,
562                                  op);
563     GNUNET_free (op);
564     return;
565   }
566   if (NULL != h->th)
567   {
568     /* request active but not yet with service, can still abort */
569     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
570                 "Client aborted head operation prior to transmission, aborting it\n");
571     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
572     h->th = NULL;
573     GNUNET_CONTAINER_DLL_remove (h->op_head,
574                                  h->op_tail,
575                                  op);
576     GNUNET_free (op);
577     transmit_next (h);
578     return;
579   }
580   /* request active with service, simply ensure continuations are not called */
581   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582               "Client aborted active request, NULLing continuation\n");
583   op->ex_cb = NULL;
584   op->iss_cb = NULL;
585 }
586
587
588 /**
589  * Disconnect from service
590  *
591  * @param h handle to destroy
592  */
593 void
594 GNUNET_IDENTITY_PROVIDER_disconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
595 {
596   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
597
598   GNUNET_assert (NULL != h);
599   if (h->reconnect_task != NULL)
600   {
601     GNUNET_SCHEDULER_cancel (h->reconnect_task);
602     h->reconnect_task = NULL;
603   }
604   if (NULL != h->th)
605   {
606     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
607     h->th = NULL;
608   }
609   while (NULL != (op = h->op_head))
610   {
611     GNUNET_CONTAINER_DLL_remove (h->op_head,
612                                  h->op_tail,
613                                  op);
614     GNUNET_free (op);
615   }
616   if (NULL != h->client)
617   {
618     GNUNET_CLIENT_disconnect (h->client);
619     h->client = NULL;
620   }
621   GNUNET_free (h);
622 }
623
624 /**
625  * Convenience API
626  */
627
628
629 /**
630  * Destroy token
631  *
632  * @param token the token
633  */
634 void
635 GNUNET_IDENTITY_PROVIDER_token_destroy(struct GNUNET_IDENTITY_PROVIDER_Token *token)
636 {
637   GNUNET_assert (NULL != token);
638   if (NULL != token->data)
639     GNUNET_free (token->data);
640   GNUNET_free (token);
641 }
642
643 /**
644  * Returns string representation of token. A JSON-Web-Token.
645  *
646  * @param token the token
647  * @return The JWT (must be freed)
648  */
649 char *
650 GNUNET_IDENTITY_PROVIDER_token_to_string (const struct GNUNET_IDENTITY_PROVIDER_Token *token)
651 {
652   return GNUNET_strdup (token->data);
653 }
654
655 /**
656  * Returns string representation of ticket. Base64-Encoded
657  *
658  * @param ticket the ticket
659  * @return the Base64-Encoded ticket
660  */
661 char *
662 GNUNET_IDENTITY_PROVIDER_ticket_to_string (const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
663 {
664   return GNUNET_strdup (ticket->data);
665 }
666
667 /**
668  * Created a ticket from a string (Base64 encoded ticket)
669  *
670  * @param input Base64 encoded ticket
671  * @param ticket pointer where the ticket is stored
672  * @return GNUNET_OK
673  */
674 int
675 GNUNET_IDENTITY_PROVIDER_string_to_ticket (const char* input,
676                                            struct GNUNET_IDENTITY_PROVIDER_Ticket **ticket)
677 {
678   *ticket = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket));
679   (*ticket)->data = GNUNET_strdup (input);
680   return GNUNET_OK;
681 }
682
683
684 /**
685  * Destroys a ticket
686  *
687  * @param ticket the ticket to destroy
688  */
689 void
690 GNUNET_IDENTITY_PROVIDER_ticket_destroy(struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
691 {
692   GNUNET_assert (NULL != ticket);
693   if (NULL != ticket->data)
694     GNUNET_free (ticket->data);
695   GNUNET_free (ticket);
696 }
697
698
699
700
701
702 /* end of identity_provider_api.c */