-doxygen
[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_mq_lib.h"
31 #include "gnunet_identity_provider_service.h"
32 #include "identity_provider.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "identity-api",__VA_ARGS__)
35
36
37
38 /**
39  * Handle for an operation with the service.
40  */
41 struct GNUNET_IDENTITY_PROVIDER_Operation
42 {
43
44   /**
45    * Main handle.
46    */
47   struct GNUNET_IDENTITY_PROVIDER_Handle *h;
48
49   /**
50    * We keep operations in a DLL.
51    */
52   struct GNUNET_IDENTITY_PROVIDER_Operation *next;
53
54   /**
55    * We keep operations in a DLL.
56    */
57   struct GNUNET_IDENTITY_PROVIDER_Operation *prev;
58
59   /**
60    * Message to send to the service.
61    * Allocated at the end of this struct.
62    */
63   const struct GNUNET_MessageHeader *msg;
64
65   /**
66    * Continuation to invoke with the result of the transmission; @e cb
67    * will be NULL in this case.
68    */
69   GNUNET_IDENTITY_PROVIDER_ExchangeCallback ex_cb;
70
71   /**
72    * Continuation to invoke with the result of the transmission for
73    * 'issue' operations (@e cont will be NULL in this case).
74    */
75   GNUNET_IDENTITY_PROVIDER_IssueCallback iss_cb;
76
77   /**
78    * Envelope with the message for this queue entry.
79    */
80   struct GNUNET_MQ_Envelope *env;
81
82   /**
83    * request id
84    */
85   uint32_t r_id;
86
87   /**
88    * Closure for @e cont or @e cb.
89    */
90   void *cls;
91
92 };
93
94
95 /**
96  * Handle for the service.
97  */
98 struct GNUNET_IDENTITY_PROVIDER_Handle
99 {
100   /**
101    * Configuration to use.
102    */
103   const struct GNUNET_CONFIGURATION_Handle *cfg;
104
105   /**
106    * Socket (if available).
107    */
108   struct GNUNET_CLIENT_Connection *client;
109
110   /**
111    * Closure for 'cb'.
112    */
113   void *cb_cls;
114
115   /**
116    * Head of active operations.
117    */
118   struct GNUNET_IDENTITY_PROVIDER_Operation *op_head;
119
120   /**
121    * Tail of active operations.
122    */
123   struct GNUNET_IDENTITY_PROVIDER_Operation *op_tail;
124
125   /**
126    * Currently pending transmission request, or NULL for none.
127    */
128   struct GNUNET_CLIENT_TransmitHandle *th;
129
130   /**
131    * Task doing exponential back-off trying to reconnect.
132    */
133   struct GNUNET_SCHEDULER_Task * reconnect_task;
134
135   /**
136    * Time for next connect retry.
137    */
138   struct GNUNET_TIME_Relative reconnect_backoff;
139
140   /**
141    * Connection to service (if available).
142    */
143   struct GNUNET_MQ_Handle *mq;
144
145   /**
146    * Request Id generator.  Incremented by one for each request.
147    */
148   uint32_t r_id_gen;
149
150   /**
151    * Are we polling for incoming messages right now?
152    */
153   int in_receive;
154
155 };
156
157
158 /**
159  * Try again to connect to the service.
160  *
161  * @param cls handle to the service.
162  */
163 static void
164 reconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *handle);
165
166 /**
167  * Reconnect
168  *
169  * @param cls the handle
170  */
171 static void
172 reconnect_task (void *cls)
173 {
174   struct GNUNET_IDENTITY_PROVIDER_Handle *handle = cls;
175
176   handle->reconnect_task = NULL;
177   reconnect (handle);
178 }
179
180
181 /**
182  * Disconnect from service and then reconnect.
183  *
184  * @param handle our handle
185  */
186 static void
187 force_reconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *handle)
188 {
189   GNUNET_MQ_destroy (handle->mq);
190   handle->mq = NULL;
191   handle->reconnect_backoff
192     = GNUNET_TIME_STD_BACKOFF (handle->reconnect_backoff);
193   handle->reconnect_task
194     = GNUNET_SCHEDULER_add_delayed (handle->reconnect_backoff,
195                                     &reconnect_task,
196                                     handle);
197 }
198
199 /**
200  * Generic error handler, called with the appropriate error code and
201  * the same closure specified at the creation of the message queue.
202  * Not every message queue implementation supports an error handler.
203  *
204  * @param cls closure with the `struct GNUNET_GNS_Handle *`
205  * @param error error code
206  */
207 static void
208 mq_error_handler (void *cls,
209                   enum GNUNET_MQ_Error error)
210 {
211   struct GNUNET_IDENTITY_PROVIDER_Handle *handle = cls;
212   force_reconnect (handle);
213 }
214
215 /**
216  * Check validity of message received from the service
217  *
218  * @param cls the `struct GNUNET_IDENTITY_PROVIDER_Handle *`
219  * @param result_msg the incoming message
220  */
221 static int
222 check_exchange_result (void *cls,
223               const struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage *erm)
224 {
225   char *str;
226   size_t size = ntohs (erm->header.size) - sizeof (*erm);
227   
228
229   str = (char *) &erm[1];
230   if ( (size > sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage)) &&
231        ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage) - 1]) )
232   {
233     GNUNET_break (0);
234     return GNUNET_SYSERR;
235   }
236   return GNUNET_OK;
237 }
238
239
240 /**
241  * Check validity of message received from the service
242  *
243  * @param cls the `struct GNUNET_IDENTITY_PROVIDER_Handle *`
244  * @param result_msg the incoming message
245  */
246 static int
247 check_result (void *cls,
248               const struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage *irm)
249 {
250   char *str;
251   size_t size = ntohs (irm->header.size) - sizeof (*irm);
252   str = (char*) &irm[1];
253   if ( (size > sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage)) &&
254        ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage) - 1]) )
255   {
256     GNUNET_break (0);
257     return GNUNET_SYSERR;
258   }
259   return GNUNET_OK;
260 }
261
262 /**
263  * Handler for messages received from the GNS service
264  *
265  * @param cls the `struct GNUNET_GNS_Handle *`
266  * @param loookup_msg the incoming message
267  */
268 static void
269 handle_exchange_result (void *cls,
270                         const struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage *erm)
271 {
272   struct GNUNET_IDENTITY_PROVIDER_Handle *handle = cls;
273   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
274   struct GNUNET_IDENTITY_PROVIDER_Token token;
275   uint64_t ticket_nonce;
276   uint32_t r_id = ntohl (erm->id);
277   char *str;
278   
279   for (op = handle->op_head; NULL != op; op = op->next)
280     if (op->r_id == r_id)
281       break;
282   if (NULL == op)
283     return;
284   str = GNUNET_strdup ((char*)&erm[1]);
285   op = handle->op_head;
286   GNUNET_CONTAINER_DLL_remove (handle->op_head,
287                                handle->op_tail,
288                                op);
289   token.data = str;
290   ticket_nonce = ntohl (erm->ticket_nonce);
291   if (NULL != op->ex_cb)
292     op->ex_cb (op->cls, &token, ticket_nonce);
293   GNUNET_free (str);
294   GNUNET_free (op);
295
296 }
297
298 /**
299  * Handler for messages received from the GNS service
300  *
301  * @param cls the `struct GNUNET_GNS_Handle *`
302  * @param loookup_msg the incoming message
303  */
304 static void
305 handle_result (void *cls,
306                const struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage *irm)
307 {
308   struct GNUNET_IDENTITY_PROVIDER_Handle *handle = cls;
309   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
310   struct GNUNET_IDENTITY_PROVIDER_Token token;
311   struct GNUNET_IDENTITY_PROVIDER_Ticket ticket;
312   uint32_t r_id = ntohl (irm->id);
313   char *str;
314   char *label_str;
315   char *ticket_str;
316   char *token_str;
317
318   for (op = handle->op_head; NULL != op; op = op->next)
319     if (op->r_id == r_id)
320       break;
321   if (NULL == op)
322     return;
323   str = GNUNET_strdup ((char*)&irm[1]);
324   label_str = strtok (str, ",");
325
326   if (NULL == label_str)
327   {
328     GNUNET_free (str);
329     GNUNET_break (0);
330     return;
331   }
332   ticket_str = strtok (NULL, ",");
333   if (NULL == ticket_str)
334   {
335     GNUNET_free (str);
336     GNUNET_break (0);
337     return;
338   }
339   token_str = strtok (NULL, ",");
340   if (NULL == token_str)
341   {
342     GNUNET_free (str);
343     GNUNET_break (0);
344     return;
345   }
346   GNUNET_CONTAINER_DLL_remove (handle->op_head,
347                                handle->op_tail,
348                                op);
349   ticket.data = ticket_str;
350   token.data = token_str;
351   if (NULL != op->iss_cb)
352     op->iss_cb (op->cls, label_str, &ticket, &token);
353   GNUNET_free (str);
354   GNUNET_free (op);
355
356 }
357
358 /**
359  * Try again to connect to the service.
360  *
361  * @param cls handle to the identity provider service.
362  */
363 static void
364 reconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
365 {
366   GNUNET_MQ_hd_var_size (result,
367                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT,
368                          struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage);
369   GNUNET_MQ_hd_var_size (exchange_result,
370                          GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT,
371                          struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage);
372   struct GNUNET_MQ_MessageHandler handlers[] = {
373     make_result_handler (h),
374     make_exchange_result_handler (h),
375     GNUNET_MQ_handler_end ()
376   };
377   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
378
379   GNUNET_assert (NULL == h->mq);
380   LOG (GNUNET_ERROR_TYPE_DEBUG,
381        "Connecting to identity provider service.\n");
382
383   h->mq = GNUNET_CLIENT_connecT (h->cfg,
384                                  "identity-provider",
385                                  handlers,
386                                  &mq_error_handler,
387                                  h);
388   if (NULL == h->mq)
389     return;
390   for (op = h->op_head; NULL != op; op = op->next)
391     GNUNET_MQ_send_copy (h->mq,
392                          op->env);
393 }
394
395
396 /**
397  * Connect to the identity provider service.
398  *
399  * @param cfg the configuration to use
400  * @return handle to use
401  */
402 struct GNUNET_IDENTITY_PROVIDER_Handle *
403 GNUNET_IDENTITY_PROVIDER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
404 {
405   struct GNUNET_IDENTITY_PROVIDER_Handle *h;
406
407   h = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Handle);
408   h->cfg = cfg;
409   reconnect (h);
410   if (NULL == h->mq)
411   {
412     GNUNET_free (h);
413     return NULL;
414   }
415   return h;
416 }
417
418
419 /**
420  * Issue an identity token
421  *
422  * @param id identity service to query
423  * @param service_name for which service is an identity wanted
424  * @param cb function to call with the result (will only be called once)
425  * @param cb_cls closure for @a cb
426  * @return handle to abort the operation
427  */
428 struct GNUNET_IDENTITY_PROVIDER_Operation *
429 GNUNET_IDENTITY_PROVIDER_issue_token (struct GNUNET_IDENTITY_PROVIDER_Handle *id,
430                                       const struct GNUNET_CRYPTO_EcdsaPrivateKey *iss_key,
431                                       const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
432                                       const char* scopes,
433                                       struct GNUNET_TIME_Absolute expiration,
434                                       uint64_t nonce,
435                                       GNUNET_IDENTITY_PROVIDER_IssueCallback cb,
436                                       void *cb_cls)
437 {
438   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
439   struct GNUNET_IDENTITY_PROVIDER_IssueMessage *im;
440   size_t slen;
441
442   slen = strlen (scopes) + 1;
443   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage))
444   {
445     GNUNET_break (0);
446     return NULL;
447   }
448   op = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Operation);
449   op->h = id;
450   op->iss_cb = cb;
451   op->cls = cb_cls;
452   op->r_id = id->r_id_gen++;
453   op->env = GNUNET_MQ_msg_extra (im,
454                                  slen,
455                                  GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE);
456   im->id = op->r_id;
457   im->iss_key = *iss_key;
458   im->aud_key = *aud_key;
459   im->nonce = htonl (nonce);
460   im->expiration = GNUNET_TIME_absolute_hton (expiration);
461   GNUNET_memcpy (&im[1], scopes, slen);
462   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
463                                     id->op_tail,
464                                     op);
465   if (NULL != id->mq)
466     GNUNET_MQ_send_copy (id->mq,
467                          op->env);
468   return op;
469 }
470
471
472 /**
473  * Exchange a token ticket for a token
474  *
475  * @param id identity provider service
476  * @param ticket ticket to exchange
477  * @param cont function to call once the operation finished
478  * @param cont_cls closure for @a cont
479  * @return handle to abort the operation
480  */
481 struct GNUNET_IDENTITY_PROVIDER_Operation *
482 GNUNET_IDENTITY_PROVIDER_exchange_ticket (struct GNUNET_IDENTITY_PROVIDER_Handle *id,
483                                           const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket,
484                                           const struct GNUNET_CRYPTO_EcdsaPrivateKey *aud_privkey,
485                                           GNUNET_IDENTITY_PROVIDER_ExchangeCallback cont,
486                                           void *cont_cls)
487 {
488   struct GNUNET_IDENTITY_PROVIDER_Operation *op;
489   struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *em;
490   size_t slen;
491   char *ticket_str;
492
493   ticket_str = GNUNET_IDENTITY_PROVIDER_ticket_to_string (ticket);
494
495   slen = strlen (ticket_str) + 1;
496   if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage))
497   {
498     GNUNET_free (ticket_str);
499     GNUNET_break (0);
500     return NULL;
501   }
502   op = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Operation);
503   op->h = id;
504   op->ex_cb = cont;
505   op->cls = cont_cls;
506   op->r_id = id->r_id_gen++;
507   op->env = GNUNET_MQ_msg_extra (em,
508                                  slen,
509                                  GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE);
510   em->aud_privkey = *aud_privkey;
511   em->id = htonl (op->r_id);
512   GNUNET_memcpy (&em[1], ticket_str, slen);
513   GNUNET_free (ticket_str);
514   GNUNET_CONTAINER_DLL_insert_tail (id->op_head,
515                                     id->op_tail,
516                                     op);
517   if (NULL != id->mq)
518     GNUNET_MQ_send_copy (id->mq,
519                          op->env);
520   return op;
521 }
522
523
524 /**
525  * Cancel an operation. Note that the operation MAY still
526  * be executed; this merely cancels the continuation; if the request
527  * was already transmitted, the service may still choose to complete
528  * the operation.
529  *
530  * @param op operation to cancel
531  */
532 void
533 GNUNET_IDENTITY_PROVIDER_cancel (struct GNUNET_IDENTITY_PROVIDER_Operation *op)
534 {
535   struct GNUNET_IDENTITY_PROVIDER_Handle *h = op->h;
536
537   GNUNET_CONTAINER_DLL_remove (h->op_head,
538                                h->op_tail,
539                                op);
540   GNUNET_MQ_discard (op->env);
541   GNUNET_free (op);
542 }
543
544
545 /**
546  * Disconnect from service
547  *
548  * @param h handle to destroy
549  */
550 void
551 GNUNET_IDENTITY_PROVIDER_disconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
552 {
553   GNUNET_assert (NULL != h);
554   if (NULL != h->mq)
555   {
556     GNUNET_MQ_destroy (h->mq);
557     h->mq = NULL;
558   }
559   if (NULL != h->reconnect_task)
560   {
561     GNUNET_SCHEDULER_cancel (h->reconnect_task);
562     h->reconnect_task = NULL;
563   }
564   GNUNET_assert (NULL == h->op_head);
565   GNUNET_free (h);
566 }
567
568 /**
569  * Convenience API
570  */
571
572
573 /**
574  * Destroy token
575  *
576  * @param token the token
577  */
578 void
579 GNUNET_IDENTITY_PROVIDER_token_destroy(struct GNUNET_IDENTITY_PROVIDER_Token *token)
580 {
581   GNUNET_assert (NULL != token);
582   if (NULL != token->data)
583     GNUNET_free (token->data);
584   GNUNET_free (token);
585 }
586
587 /**
588  * Returns string representation of token. A JSON-Web-Token.
589  *
590  * @param token the token
591  * @return The JWT (must be freed)
592  */
593 char *
594 GNUNET_IDENTITY_PROVIDER_token_to_string (const struct GNUNET_IDENTITY_PROVIDER_Token *token)
595 {
596   return GNUNET_strdup (token->data);
597 }
598
599 /**
600  * Returns string representation of ticket. Base64-Encoded
601  *
602  * @param ticket the ticket
603  * @return the Base64-Encoded ticket
604  */
605 char *
606 GNUNET_IDENTITY_PROVIDER_ticket_to_string (const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
607 {
608   return GNUNET_strdup (ticket->data);
609 }
610
611 /**
612  * Created a ticket from a string (Base64 encoded ticket)
613  *
614  * @param input Base64 encoded ticket
615  * @param ticket pointer where the ticket is stored
616  * @return GNUNET_OK
617  */
618 int
619 GNUNET_IDENTITY_PROVIDER_string_to_ticket (const char* input,
620                                            struct GNUNET_IDENTITY_PROVIDER_Ticket **ticket)
621 {
622   *ticket = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket));
623   (*ticket)->data = GNUNET_strdup (input);
624   return GNUNET_OK;
625 }
626
627
628 /**
629  * Destroys a ticket
630  *
631  * @param ticket the ticket to destroy
632  */
633 void
634 GNUNET_IDENTITY_PROVIDER_ticket_destroy(struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
635 {
636   GNUNET_assert (NULL != ticket);
637   if (NULL != ticket->data)
638     GNUNET_free (ticket->data);
639   GNUNET_free (ticket);
640 }
641
642
643
644
645
646 /* end of identity_provider_api.c */