2 This file is part of GNUnet.
3 Copyright (C) 2016 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file identity-provider/identity_provider_api.c
23 * @brief api to interact with the identity provider service
24 * @author Martin Schanzenbach
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"
33 #define LOG(kind,...) GNUNET_log_from (kind, "identity-api",__VA_ARGS__)
38 * Handle for an operation with the service.
40 struct GNUNET_IDENTITY_PROVIDER_Operation
46 struct GNUNET_IDENTITY_PROVIDER_Handle *h;
49 * We keep operations in a DLL.
51 struct GNUNET_IDENTITY_PROVIDER_Operation *next;
54 * We keep operations in a DLL.
56 struct GNUNET_IDENTITY_PROVIDER_Operation *prev;
59 * Message to send to the service.
60 * Allocated at the end of this struct.
62 const struct GNUNET_MessageHeader *msg;
65 * Continuation to invoke with the result of the transmission; @e cb
66 * will be NULL in this case.
68 GNUNET_IDENTITY_PROVIDER_ExchangeCallback ex_cb;
71 * Continuation to invoke with the result of the transmission for
72 * 'issue' operations (@e cont will be NULL in this case).
74 GNUNET_IDENTITY_PROVIDER_IssueCallback iss_cb;
77 * Closure for @e cont or @e cb.
85 * Handle for the service.
87 struct GNUNET_IDENTITY_PROVIDER_Handle
90 * Configuration to use.
92 const struct GNUNET_CONFIGURATION_Handle *cfg;
95 * Socket (if available).
97 struct GNUNET_CLIENT_Connection *client;
105 * Head of active operations.
107 struct GNUNET_IDENTITY_PROVIDER_Operation *op_head;
110 * Tail of active operations.
112 struct GNUNET_IDENTITY_PROVIDER_Operation *op_tail;
115 * Currently pending transmission request, or NULL for none.
117 struct GNUNET_CLIENT_TransmitHandle *th;
120 * Task doing exponential back-off trying to reconnect.
122 struct GNUNET_SCHEDULER_Task * reconnect_task;
125 * Time for next connect retry.
127 struct GNUNET_TIME_Relative reconnect_delay;
130 * Are we polling for incoming messages right now?
138 * Try again to connect to the service.
140 * @param cls handle to the service.
141 * @param tc scheduler context
144 reconnect (void *cls,
145 const struct GNUNET_SCHEDULER_TaskContext *tc);
149 * Reschedule a connect attempt to the service.
151 * @param h transport service to reconnect
154 reschedule_connect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
156 GNUNET_assert (h->reconnect_task == NULL);
160 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
163 if (NULL != h->client)
165 GNUNET_CLIENT_disconnect (h->client);
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));
173 GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
174 h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
179 * Type of a function to call when we receive a message
183 * @param msg message received, NULL on timeout or fatal error
186 message_handler (void *cls,
187 const struct GNUNET_MessageHeader *msg)
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;
203 reschedule_connect (h);
206 LOG (GNUNET_ERROR_TYPE_DEBUG,
207 "Received message of type %d from identity provider service\n",
209 size = ntohs (msg->size);
210 switch (ntohs (msg->type))
212 case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT:
213 if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
216 reschedule_connect (h);
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]) )
226 reschedule_connect (h);
229 if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
234 label_str = strtok (str, ",");
236 if (NULL == label_str)
240 reschedule_connect (h);
243 ticket_str = strtok (NULL, ",");
244 if (NULL == ticket_str)
248 reschedule_connect (h);
251 token_str = strtok (NULL, ",");
252 if (NULL == token_str)
256 reschedule_connect (h);
260 GNUNET_CONTAINER_DLL_remove (h->op_head,
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);
272 case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT:
273 if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
276 reschedule_connect (h);
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]) )
285 reschedule_connect (h);
288 if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
292 GNUNET_CONTAINER_DLL_remove (h->op_head,
295 GNUNET_CLIENT_receive (h->client, &message_handler, h,
296 GNUNET_TIME_UNIT_FOREVER_REL);
298 if (NULL != op->ex_cb)
299 op->ex_cb (op->cls, &token);
305 reschedule_connect (h);
312 * Schedule transmission of the next message from our queue.
314 * @param h identity handle
317 transmit_next (struct GNUNET_IDENTITY_PROVIDER_Handle *h);
321 * Transmit next message to service.
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
329 send_next_message (void *cls,
333 struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
334 struct GNUNET_IDENTITY_PROVIDER_Operation *op = h->op_head;
340 ret = ntohs (op->msg->size);
343 reschedule_connect (h);
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) )
353 GNUNET_CONTAINER_DLL_remove (h->op_head,
359 if (GNUNET_NO == h->in_receive)
361 h->in_receive = GNUNET_YES;
362 GNUNET_CLIENT_receive (h->client,
364 GNUNET_TIME_UNIT_FOREVER_REL);
371 * Schedule transmission of the next message from our queue.
373 * @param h identity provider handle
376 transmit_next (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
378 struct GNUNET_IDENTITY_PROVIDER_Operation *op = h->op_head;
380 GNUNET_assert (NULL == h->th);
383 if (NULL == h->client)
385 h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
386 ntohs (op->msg->size),
387 GNUNET_TIME_UNIT_FOREVER_REL,
395 * Try again to connect to the service.
397 * @param cls handle to the identity provider service.
398 * @param tc scheduler context
401 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
403 struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
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);
412 GNUNET_assert (NULL != h->th);
417 * Connect to the identity provider service.
419 * @param cfg the configuration to use
420 * @return handle to use
422 struct GNUNET_IDENTITY_PROVIDER_Handle *
423 GNUNET_IDENTITY_PROVIDER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
425 struct GNUNET_IDENTITY_PROVIDER_Handle *h;
427 h = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Handle);
429 h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
430 h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
436 * Issue an identity token
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
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,
449 struct GNUNET_TIME_Absolute expiration,
451 GNUNET_IDENTITY_PROVIDER_IssueCallback cb,
454 struct GNUNET_IDENTITY_PROVIDER_Operation *op;
455 struct GNUNET_IDENTITY_PROVIDER_IssueMessage *im;
458 slen = strlen (scopes) + 1;
459 if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage))
464 op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
465 sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage) +
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) +
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,
490 * Exchange a token ticket for a token
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
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,
505 struct GNUNET_IDENTITY_PROVIDER_Operation *op;
506 struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *em;
510 ticket_str = GNUNET_IDENTITY_PROVIDER_ticket_to_string (ticket);
512 slen = strlen (ticket_str) + 1;
513 if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage))
518 op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
519 sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage) +
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) +
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,
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
547 * @param op operation to cancel
550 GNUNET_IDENTITY_PROVIDER_cancel (struct GNUNET_IDENTITY_PROVIDER_Operation *op)
552 struct GNUNET_IDENTITY_PROVIDER_Handle *h = op->h;
554 if ( (h->op_head != op) ||
555 (NULL == h->client) )
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,
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);
573 GNUNET_CONTAINER_DLL_remove (h->op_head,
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");
589 * Disconnect from service
591 * @param h handle to destroy
594 GNUNET_IDENTITY_PROVIDER_disconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
596 struct GNUNET_IDENTITY_PROVIDER_Operation *op;
598 GNUNET_assert (NULL != h);
599 if (h->reconnect_task != NULL)
601 GNUNET_SCHEDULER_cancel (h->reconnect_task);
602 h->reconnect_task = NULL;
606 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
609 while (NULL != (op = h->op_head))
611 GNUNET_CONTAINER_DLL_remove (h->op_head,
616 if (NULL != h->client)
618 GNUNET_CLIENT_disconnect (h->client);
632 * @param token the token
635 GNUNET_IDENTITY_PROVIDER_token_destroy(struct GNUNET_IDENTITY_PROVIDER_Token *token)
637 GNUNET_assert (NULL != token);
638 if (NULL != token->data)
639 GNUNET_free (token->data);
644 * Returns string representation of token. A JSON-Web-Token.
646 * @param token the token
647 * @return The JWT (must be freed)
650 GNUNET_IDENTITY_PROVIDER_token_to_string (const struct GNUNET_IDENTITY_PROVIDER_Token *token)
652 return GNUNET_strdup (token->data);
656 * Returns string representation of ticket. Base64-Encoded
658 * @param ticket the ticket
659 * @return the Base64-Encoded ticket
662 GNUNET_IDENTITY_PROVIDER_ticket_to_string (const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
664 return GNUNET_strdup (ticket->data);
668 * Created a ticket from a string (Base64 encoded ticket)
670 * @param input Base64 encoded ticket
671 * @param ticket pointer where the ticket is stored
675 GNUNET_IDENTITY_PROVIDER_string_to_ticket (const char* input,
676 struct GNUNET_IDENTITY_PROVIDER_Ticket **ticket)
678 *ticket = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket));
679 (*ticket)->data = GNUNET_strdup (input);
687 * @param ticket the ticket to destroy
690 GNUNET_IDENTITY_PROVIDER_ticket_destroy(struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
692 GNUNET_assert (NULL != ticket);
693 if (NULL != ticket->data)
694 GNUNET_free (ticket->data);
695 GNUNET_free (ticket);
702 /* end of identity_provider_api.c */