2 This file is part of GNUnet.
3 Copyright (C) 2016 GNUnet e.V.
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.
143 reconnect (void *cls);
147 * Reschedule a connect attempt to the service.
149 * @param h transport service to reconnect
152 reschedule_connect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
154 GNUNET_assert (h->reconnect_task == NULL);
158 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
161 if (NULL != h->client)
163 GNUNET_CLIENT_disconnect (h->client);
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));
171 GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
172 h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
177 * Type of a function to call when we receive a message
181 * @param msg message received, NULL on timeout or fatal error
184 message_handler (void *cls,
185 const struct GNUNET_MessageHeader *msg)
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;
198 uint64_t ticket_nonce;
202 reschedule_connect (h);
205 LOG (GNUNET_ERROR_TYPE_DEBUG,
206 "Received message of type %d from identity provider service\n",
208 size = ntohs (msg->size);
209 switch (ntohs (msg->type))
211 case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_ISSUE_RESULT:
212 if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
215 reschedule_connect (h);
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]) )
225 reschedule_connect (h);
228 if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueResultMessage))
233 label_str = strtok (str, ",");
235 if (NULL == label_str)
239 reschedule_connect (h);
242 ticket_str = strtok (NULL, ",");
243 if (NULL == ticket_str)
247 reschedule_connect (h);
250 token_str = strtok (NULL, ",");
251 if (NULL == token_str)
255 reschedule_connect (h);
259 GNUNET_CONTAINER_DLL_remove (h->op_head,
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);
271 case GNUNET_MESSAGE_TYPE_IDENTITY_PROVIDER_EXCHANGE_RESULT:
272 if (size < sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
275 reschedule_connect (h);
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]) )
284 reschedule_connect (h);
287 if (size == sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeResultMessage))
291 GNUNET_CONTAINER_DLL_remove (h->op_head,
294 GNUNET_CLIENT_receive (h->client, &message_handler, h,
295 GNUNET_TIME_UNIT_FOREVER_REL);
297 ticket_nonce = ntohl (erm->ticket_nonce);
298 if (NULL != op->ex_cb)
299 op->ex_cb (op->cls, &token, ticket_nonce);
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.
400 reconnect (void *cls)
402 struct GNUNET_IDENTITY_PROVIDER_Handle *h = cls;
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);
411 GNUNET_assert (NULL != h->th);
416 * Connect to the identity provider service.
418 * @param cfg the configuration to use
419 * @return handle to use
421 struct GNUNET_IDENTITY_PROVIDER_Handle *
422 GNUNET_IDENTITY_PROVIDER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
424 struct GNUNET_IDENTITY_PROVIDER_Handle *h;
426 h = GNUNET_new (struct GNUNET_IDENTITY_PROVIDER_Handle);
428 h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
429 h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
435 * Issue an identity token
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
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,
448 struct GNUNET_TIME_Absolute expiration,
450 GNUNET_IDENTITY_PROVIDER_IssueCallback cb,
453 struct GNUNET_IDENTITY_PROVIDER_Operation *op;
454 struct GNUNET_IDENTITY_PROVIDER_IssueMessage *im;
457 slen = strlen (scopes) + 1;
458 if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage))
463 op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
464 sizeof (struct GNUNET_IDENTITY_PROVIDER_IssueMessage) +
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) +
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,
489 * Exchange a token ticket for a token
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
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,
504 struct GNUNET_IDENTITY_PROVIDER_Operation *op;
505 struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage *em;
509 ticket_str = GNUNET_IDENTITY_PROVIDER_ticket_to_string (ticket);
511 slen = strlen (ticket_str) + 1;
512 if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage))
517 op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Operation) +
518 sizeof (struct GNUNET_IDENTITY_PROVIDER_ExchangeMessage) +
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) +
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,
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
546 * @param op operation to cancel
549 GNUNET_IDENTITY_PROVIDER_cancel (struct GNUNET_IDENTITY_PROVIDER_Operation *op)
551 struct GNUNET_IDENTITY_PROVIDER_Handle *h = op->h;
553 if ( (h->op_head != op) ||
554 (NULL == h->client) )
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,
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);
572 GNUNET_CONTAINER_DLL_remove (h->op_head,
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");
588 * Disconnect from service
590 * @param h handle to destroy
593 GNUNET_IDENTITY_PROVIDER_disconnect (struct GNUNET_IDENTITY_PROVIDER_Handle *h)
595 struct GNUNET_IDENTITY_PROVIDER_Operation *op;
597 GNUNET_assert (NULL != h);
598 if (h->reconnect_task != NULL)
600 GNUNET_SCHEDULER_cancel (h->reconnect_task);
601 h->reconnect_task = NULL;
605 GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
608 while (NULL != (op = h->op_head))
610 GNUNET_CONTAINER_DLL_remove (h->op_head,
615 if (NULL != h->client)
617 GNUNET_CLIENT_disconnect (h->client);
631 * @param token the token
634 GNUNET_IDENTITY_PROVIDER_token_destroy(struct GNUNET_IDENTITY_PROVIDER_Token *token)
636 GNUNET_assert (NULL != token);
637 if (NULL != token->data)
638 GNUNET_free (token->data);
643 * Returns string representation of token. A JSON-Web-Token.
645 * @param token the token
646 * @return The JWT (must be freed)
649 GNUNET_IDENTITY_PROVIDER_token_to_string (const struct GNUNET_IDENTITY_PROVIDER_Token *token)
651 return GNUNET_strdup (token->data);
655 * Returns string representation of ticket. Base64-Encoded
657 * @param ticket the ticket
658 * @return the Base64-Encoded ticket
661 GNUNET_IDENTITY_PROVIDER_ticket_to_string (const struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
663 return GNUNET_strdup (ticket->data);
667 * Created a ticket from a string (Base64 encoded ticket)
669 * @param input Base64 encoded ticket
670 * @param ticket pointer where the ticket is stored
674 GNUNET_IDENTITY_PROVIDER_string_to_ticket (const char* input,
675 struct GNUNET_IDENTITY_PROVIDER_Ticket **ticket)
677 *ticket = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_PROVIDER_Ticket));
678 (*ticket)->data = GNUNET_strdup (input);
686 * @param ticket the ticket to destroy
689 GNUNET_IDENTITY_PROVIDER_ticket_destroy(struct GNUNET_IDENTITY_PROVIDER_Ticket *ticket)
691 GNUNET_assert (NULL != ticket);
692 if (NULL != ticket->data)
693 GNUNET_free (ticket->data);
694 GNUNET_free (ticket);
701 /* end of identity_provider_api.c */