webticket: implement stack shutdown flag
[siplcs.git] / src / core / sipe-webticket.c
blob213616132f1c86133eb1970d3c19891839fca429
1 /**
2 * @file sipe-webticket.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Specification references:
26 * - [MS-OCAUTHWS]: http://msdn.microsoft.com/en-us/library/ff595592.aspx
27 * - MS Tech-Ed Europe 2010 "UNC310: Microsoft Lync 2010 Technology Explained"
28 * http://ecn.channel9.msdn.com/o9/te/Europe/2010/pptx/unc310.pptx
31 #include <string.h>
32 #include <time.h>
34 #include <glib.h>
36 #include "sipe-common.h"
37 #include "sipe-backend.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-digest.h"
41 #include "sipe-svc.h"
42 #include "sipe-tls.h"
43 #include "sipe-webticket.h"
44 #include "sipe-utils.h"
45 #include "sipe-xml.h"
47 struct webticket_queued_data {
48 sipe_webticket_callback *callback;
49 gpointer callback_data;
52 struct webticket_callback_data {
53 gchar *service_uri;
54 const gchar *service_port;
55 gchar *service_auth_uri;
57 gchar *webticket_negotiate_uri;
58 gchar *webticket_fedbearer_uri;
60 gboolean tried_fedbearer;
61 gboolean requires_signing;
62 enum {
63 TOKEN_STATE_NONE = 0,
64 TOKEN_STATE_SERVICE,
65 TOKEN_STATE_FEDERATION,
66 TOKEN_STATE_FED_BEARER,
67 } token_state;
69 struct sipe_tls_random entropy;
71 sipe_webticket_callback *callback;
72 gpointer callback_data;
74 struct sipe_svc_session *session;
76 GSList *queued;
79 struct webticket_token {
80 gchar *auth_uri;
81 gchar *token;
82 time_t expires;
85 struct sipe_webticket {
86 GHashTable *cache;
87 GHashTable *pending;
89 gchar *webticket_adfs_uri;
90 gchar *adfs_token;
91 time_t adfs_token_expires;
93 gboolean retrieved_realminfo;
94 gboolean shutting_down;
97 void sipe_webticket_free(struct sipe_core_private *sipe_private)
99 struct sipe_webticket *webticket = sipe_private->webticket;
100 if (!webticket)
101 return;
103 /* Web Ticket stack is shutting down: reject all new requests */
104 webticket->shutting_down = TRUE;
106 g_free(webticket->webticket_adfs_uri);
107 g_free(webticket->adfs_token);
108 if (webticket->pending)
109 g_hash_table_destroy(webticket->pending);
110 if (webticket->cache)
111 g_hash_table_destroy(webticket->cache);
112 g_free(webticket);
113 sipe_private->webticket = NULL;
116 static void free_token(gpointer data)
118 struct webticket_token *wt = data;
119 g_free(wt->auth_uri);
120 g_free(wt->token);
121 g_free(wt);
124 static void sipe_webticket_init(struct sipe_core_private *sipe_private)
126 struct sipe_webticket *webticket;
128 if (sipe_private->webticket)
129 return;
131 sipe_private->webticket = webticket = g_new0(struct sipe_webticket, 1);
133 webticket->cache = g_hash_table_new_full(g_str_hash,
134 g_str_equal,
135 g_free,
136 free_token);
137 webticket->pending = g_hash_table_new(g_str_hash,
138 g_str_equal);
141 /* takes ownership of "token" */
142 static void cache_token(struct sipe_core_private *sipe_private,
143 const gchar *service_uri,
144 const gchar *auth_uri,
145 gchar *token,
146 time_t expires)
148 struct webticket_token *wt = g_new0(struct webticket_token, 1);
149 wt->auth_uri = g_strdup(auth_uri);
150 wt->token = token;
151 wt->expires = expires;
152 g_hash_table_insert(sipe_private->webticket->cache,
153 g_strdup(service_uri),
154 wt);
157 static const struct webticket_token *cache_hit(struct sipe_core_private *sipe_private,
158 const gchar *service_uri)
160 const struct webticket_token *wt;
162 /* make sure a cached Web Ticket is still valid for 60 seconds */
163 wt = g_hash_table_lookup(sipe_private->webticket->cache,
164 service_uri);
165 if (wt && (wt->expires < time(NULL) + 60)) {
166 SIPE_DEBUG_INFO("cache_hit: cached token for URI %s has expired",
167 service_uri);
168 wt = NULL;
171 return(wt);
174 /* frees just the main request data, when this is called "queued" is cleared */
175 static void callback_data_free(struct webticket_callback_data *wcd)
177 if (wcd) {
178 sipe_tls_free_random(&wcd->entropy);
179 g_free(wcd->webticket_negotiate_uri);
180 g_free(wcd->webticket_fedbearer_uri);
181 g_free(wcd->service_auth_uri);
182 g_free(wcd->service_uri);
183 g_free(wcd);
187 static void queue_request(struct webticket_callback_data *wcd,
188 sipe_webticket_callback *callback,
189 gpointer callback_data)
191 struct webticket_queued_data *wqd = g_new0(struct webticket_queued_data, 1);
193 wqd->callback = callback;
194 wqd->callback_data = callback_data;
196 wcd->queued = g_slist_prepend(wcd->queued, wqd);
199 static void callback_execute(struct sipe_core_private *sipe_private,
200 struct webticket_callback_data *wcd,
201 const gchar *auth_uri,
202 const gchar *wsse_security,
203 const gchar *failure_msg)
205 GSList *entry = wcd->queued;
207 /* complete main request */
208 wcd->callback(sipe_private,
209 wcd->service_uri,
210 auth_uri,
211 wsse_security,
212 failure_msg,
213 wcd->callback_data);
215 /* complete queued requests */
216 while (entry) {
217 struct webticket_queued_data *wqd = entry->data;
219 SIPE_DEBUG_INFO("callback_execute: completing queue request URI %s (Auth URI %s)",
220 wcd->service_uri, auth_uri);
221 wqd->callback(sipe_private,
222 wcd->service_uri,
223 auth_uri,
224 wsse_security,
225 failure_msg,
226 wqd->callback_data);
228 g_free(wqd);
229 entry = entry->next;
231 g_slist_free(wcd->queued);
233 /* drop request from pending hash */
234 g_hash_table_remove(sipe_private->webticket->pending,
235 wcd->service_uri);
238 static gchar *extract_raw_xml_attribute(const gchar *xml,
239 const gchar *name)
241 gchar *attr_start = g_strdup_printf("%s=\"", name);
242 gchar *data = NULL;
243 const gchar *start = strstr(xml, attr_start);
245 if (start) {
246 const gchar *value = start + strlen(attr_start);
247 const gchar *end = strchr(value, '"');
248 if (end) {
249 data = g_strndup(value, end - value);
253 g_free(attr_start);
254 return(data);
257 static gchar *generate_timestamp(const gchar *raw,
258 const gchar *lifetime_tag)
260 gchar *lifetime = sipe_xml_extract_raw(raw, lifetime_tag, FALSE);
261 gchar *timestamp = NULL;
262 if (lifetime)
263 timestamp = g_strdup_printf("<wsu:Timestamp xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" wsu:Id=\"timestamp\">%s</wsu:Timestamp>",
264 lifetime);
265 g_free(lifetime);
266 return(timestamp);
269 static gchar *generate_fedbearer_wsse(const gchar *raw)
271 gchar *timestamp = generate_timestamp(raw, "wst:Lifetime");
272 gchar *keydata = sipe_xml_extract_raw(raw, "EncryptedData", TRUE);
273 gchar *wsse_security = NULL;
275 if (timestamp && keydata) {
276 SIPE_DEBUG_INFO_NOFORMAT("generate_fedbearer_wsse: found timestamp & keydata");
277 wsse_security = g_strconcat(timestamp, keydata, NULL);
280 g_free(keydata);
281 g_free(timestamp);
282 return(wsse_security);
285 static void generate_federation_wsse(struct sipe_webticket *webticket,
286 const gchar *raw)
288 gchar *timestamp = generate_timestamp(raw, "t:Lifetime");
289 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
292 /* clear old ADFS token */
293 g_free(webticket->adfs_token);
294 webticket->adfs_token = NULL;
296 if (timestamp && keydata) {
297 gchar *expires_string = sipe_xml_extract_raw(timestamp,
298 "wsu:Expires",
299 FALSE);
301 if (expires_string) {
303 SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
304 expires_string);
306 /* cache ADFS token */
307 webticket->adfs_token = g_strconcat(timestamp,
308 keydata,
309 NULL);
310 webticket->adfs_token_expires = sipe_utils_str_to_time(expires_string);
311 g_free(expires_string);
315 g_free(keydata);
316 g_free(timestamp);
319 static gchar *generate_sha1_proof_wsse(const gchar *raw,
320 struct sipe_tls_random *entropy,
321 time_t *expires)
323 gchar *timestamp = generate_timestamp(raw, "Lifetime");
324 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
325 gchar *wsse_security = NULL;
327 if (timestamp && keydata) {
328 gchar *expires_string = sipe_xml_extract_raw(timestamp,
329 "Expires",
330 FALSE);
332 if (entropy) {
333 gchar *assertionID = extract_raw_xml_attribute(keydata,
334 "AssertionID");
337 * WS-Trust 1.3
339 * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1:
341 * "The key is computed using P_SHA1() from the TLS sepcification to generate
342 * a bit stream using entropy from both sides. The exact form is:
344 * key = P_SHA1(Entropy_REQ, Entropy_RES)"
346 gchar *entropy_res_base64 = sipe_xml_extract_raw(raw, "BinarySecret", FALSE);
347 gsize entropy_res_length;
348 guchar *entropy_response = g_base64_decode(entropy_res_base64,
349 &entropy_res_length);
350 guchar *key = sipe_tls_p_sha1(entropy->buffer,
351 entropy->length,
352 entropy_response,
353 entropy_res_length,
354 entropy->length);
355 g_free(entropy_response);
356 g_free(entropy_res_base64);
358 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata");
360 if (assertionID && key) {
361 /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */
362 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
363 gchar *base64;
364 gchar *signed_info;
365 gchar *canon;
367 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key");
369 /* Digest over reference element (#timestamp -> wsu:Timestamp) */
370 sipe_digest_sha1((guchar *) timestamp,
371 strlen(timestamp),
372 digest);
373 base64 = g_base64_encode(digest,
374 SIPE_DIGEST_SHA1_LENGTH);
376 /* XML-Sig: SignedInfo for reference element */
377 signed_info = g_strdup_printf("<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
378 "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
379 "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"/>"
380 "<Reference URI=\"#timestamp\">"
381 "<Transforms>"
382 "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
383 "</Transforms>"
384 "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>"
385 "<DigestValue>%s</DigestValue>"
386 "</Reference>"
387 "</SignedInfo>",
388 base64);
389 g_free(base64);
391 /* XML-Sig: SignedInfo in canonical form */
392 canon = sipe_xml_exc_c14n(signed_info);
393 g_free(signed_info);
395 if (canon) {
396 gchar *signature;
398 /* calculate signature */
399 sipe_digest_hmac_sha1(key, entropy->length,
400 (guchar *)canon,
401 strlen(canon),
402 digest);
403 base64 = g_base64_encode(digest,
404 SIPE_DIGEST_HMAC_SHA1_LENGTH);
406 /* XML-Sig: Signature from SignedInfo + Key */
407 signature = g_strdup_printf("<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
408 " %s"
409 " <SignatureValue>%s</SignatureValue>"
410 " <KeyInfo>"
411 " <wsse:SecurityTokenReference wsse:TokenType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1\">"
412 " <wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID\">%s</wsse:KeyIdentifier>"
413 " </wsse:SecurityTokenReference>"
414 " </KeyInfo>"
415 "</Signature>",
416 canon,
417 base64,
418 assertionID);
419 g_free(base64);
420 g_free(canon);
422 wsse_security = g_strconcat(timestamp,
423 keydata,
424 signature,
425 NULL);
426 g_free(signature);
431 g_free(key);
432 g_free(assertionID);
433 } else {
434 /* token doesn't require signature */
435 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required");
436 wsse_security = g_strconcat(timestamp,
437 keydata,
438 NULL);
441 *expires = 0;
442 if (expires_string) {
443 *expires = sipe_utils_str_to_time(expires_string);
444 g_free(expires_string);
448 g_free(keydata);
449 g_free(timestamp);
450 return(wsse_security);
453 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
454 struct webticket_callback_data *wcd);
455 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
456 struct webticket_callback_data *wcd);
457 static void webticket_token(struct sipe_core_private *sipe_private,
458 const gchar *uri,
459 const gchar *raw,
460 sipe_xml *soap_body,
461 gpointer callback_data)
463 struct webticket_callback_data *wcd = callback_data;
464 gboolean failed = TRUE;
466 if (soap_body) {
467 switch (wcd->token_state) {
468 case TOKEN_STATE_NONE:
469 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen...");
470 break;
472 case TOKEN_STATE_SERVICE: {
473 /* WebTicket for Web Service */
474 time_t expires;
475 gchar *wsse_security = generate_sha1_proof_wsse(raw,
476 wcd->requires_signing ? &wcd->entropy : NULL,
477 &expires);
479 if (wsse_security) {
480 /* cache takes ownership of wsse_security */
481 cache_token(sipe_private,
482 wcd->service_uri,
483 wcd->service_auth_uri,
484 wsse_security,
485 expires);
486 callback_execute(sipe_private,
487 wcd,
488 wcd->service_auth_uri,
489 wsse_security,
490 NULL);
491 failed = FALSE;
493 break;
496 case TOKEN_STATE_FEDERATION:
497 /* WebTicket from ADFS for federated authentication */
498 generate_federation_wsse(sipe_private->webticket,
499 raw);
501 if (sipe_private->webticket->adfs_token) {
503 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s",
504 uri);
506 if (federated_authentication(sipe_private,
507 wcd)) {
508 /* callback data passed down the line */
509 wcd = NULL;
512 break;
514 case TOKEN_STATE_FED_BEARER: {
515 /* WebTicket for federated authentication */
516 gchar *wsse_security = generate_fedbearer_wsse(raw);
518 if (wsse_security) {
520 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
521 uri);
523 if (sipe_svc_webticket(sipe_private,
524 wcd->session,
525 wcd->webticket_fedbearer_uri,
526 wsse_security,
527 wcd->service_auth_uri,
528 &wcd->entropy,
529 webticket_token,
530 wcd)) {
531 wcd->token_state = TOKEN_STATE_SERVICE;
533 /* callback data passed down the line */
534 wcd = NULL;
536 g_free(wsse_security);
538 break;
541 /* end of: switch (wcd->token_state) { */
544 } else if (uri) {
545 /* Retry with federated authentication? */
546 if (wcd->webticket_fedbearer_uri && !wcd->tried_fedbearer) {
547 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
548 uri);
550 if (initiate_fedbearer(sipe_private, wcd)) {
551 /* callback data passed down the line */
552 wcd = NULL;
557 if (wcd) {
558 if (failed) {
559 gchar *failure_msg = NULL;
561 if (soap_body) {
562 failure_msg = sipe_xml_data(sipe_xml_child(soap_body,
563 "Body/Fault/Detail/error/internalerror/text"));
564 /* XML data can end in &#x000D;&#x000A; */
565 g_strstrip(failure_msg);
568 callback_execute(sipe_private,
569 wcd,
570 uri,
571 NULL,
572 failure_msg);
573 g_free(failure_msg);
575 callback_data_free(wcd);
579 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
580 struct webticket_callback_data *wcd)
582 gboolean success;
584 if ((success = sipe_svc_webticket_lmc_federated(sipe_private,
585 wcd->session,
586 sipe_private->webticket->adfs_token,
587 wcd->webticket_fedbearer_uri,
588 webticket_token,
589 wcd)))
590 wcd->token_state = TOKEN_STATE_FED_BEARER;
592 /* If TRUE then callback data has been passed down the line */
593 return(success);
596 static gboolean fedbearer_authentication(struct sipe_core_private *sipe_private,
597 struct webticket_callback_data *wcd)
599 struct sipe_webticket *webticket = sipe_private->webticket;
600 gboolean success;
602 /* make sure a cached ADFS token is still valid for 60 seconds */
603 if (webticket->adfs_token &&
604 (webticket->adfs_token_expires >= time(NULL) + 60)) {
606 SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token");
607 success = federated_authentication(sipe_private, wcd);
609 } else if (webticket->webticket_adfs_uri) {
610 if ((success = sipe_svc_webticket_adfs(sipe_private,
611 wcd->session,
612 webticket->webticket_adfs_uri,
613 webticket_token,
614 wcd)))
615 wcd->token_state = TOKEN_STATE_FEDERATION;
616 } else {
617 if ((success = sipe_svc_webticket_lmc(sipe_private,
618 wcd->session,
619 wcd->webticket_fedbearer_uri,
620 webticket_token,
621 wcd)))
622 wcd->token_state = TOKEN_STATE_FED_BEARER;
625 /* If TRUE then callback data has been passed down the line */
626 return(success);
629 static void realminfo(struct sipe_core_private *sipe_private,
630 const gchar *uri,
631 SIPE_UNUSED_PARAMETER const gchar *raw,
632 sipe_xml *realminfo,
633 gpointer callback_data)
635 struct sipe_webticket *webticket = sipe_private->webticket;
636 struct webticket_callback_data *wcd = callback_data;
638 /* Only try retrieving of RealmInfo once */
639 webticket->retrieved_realminfo = TRUE;
642 * We must specifically check for abort, because
643 * realminfo == NULL is a valid response
645 if (uri) {
646 if (realminfo) {
647 /* detect ADFS setup. See also:
649 * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services
651 * NOTE: this is based on observed behaviour.
652 * It is unkown if this is documented somewhere...
654 SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully",
655 sipe_private->username);
657 webticket->webticket_adfs_uri = sipe_xml_data(sipe_xml_child(realminfo,
658 "STSAuthURL"));
661 if (webticket->webticket_adfs_uri)
662 SIPE_DEBUG_INFO("realminfo: ADFS setup detected: %s",
663 webticket->webticket_adfs_uri);
664 else
665 SIPE_DEBUG_INFO_NOFORMAT("realminfo: no RealmInfo found or no ADFS setup detected - try direct login");
667 if (fedbearer_authentication(sipe_private, wcd)) {
668 /* callback data passed down the line */
669 wcd = NULL;
673 if (wcd) {
674 callback_execute(sipe_private,
675 wcd,
676 uri,
677 NULL,
678 NULL);
679 callback_data_free(wcd);
683 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
684 struct webticket_callback_data *wcd)
686 gboolean success;
688 if (sipe_private->webticket->retrieved_realminfo) {
689 /* skip retrieval and go to authentication */
690 success = fedbearer_authentication(sipe_private, wcd);
691 } else {
692 success = sipe_svc_realminfo(sipe_private,
693 wcd->session,
694 realminfo,
695 wcd);
698 wcd->tried_fedbearer = TRUE;
700 return(success);
703 static void webticket_metadata(struct sipe_core_private *sipe_private,
704 const gchar *uri,
705 SIPE_UNUSED_PARAMETER const gchar *raw,
706 sipe_xml *metadata,
707 gpointer callback_data)
709 struct webticket_callback_data *wcd = callback_data;
711 if (metadata) {
712 const sipe_xml *node;
714 SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully",
715 uri);
717 /* Authentication ports accepted by WebTicket Service */
718 for (node = sipe_xml_child(metadata, "service/port");
719 node;
720 node = sipe_xml_twin(node)) {
721 const gchar *auth_uri = sipe_xml_attribute(sipe_xml_child(node,
722 "address"),
723 "location");
725 if (auth_uri) {
726 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
727 "WebTicketServiceWinNegotiate")) {
728 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Windows Negotiate Auth URI %s", auth_uri);
729 g_free(wcd->webticket_negotiate_uri);
730 wcd->webticket_negotiate_uri = g_strdup(auth_uri);
731 } else if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
732 "WsFedBearer")) {
733 SIPE_DEBUG_INFO("webticket_metadata: WebTicket FedBearer Auth URI %s", auth_uri);
734 g_free(wcd->webticket_fedbearer_uri);
735 wcd->webticket_fedbearer_uri = g_strdup(auth_uri);
740 if (wcd->webticket_negotiate_uri || wcd->webticket_fedbearer_uri) {
741 gboolean success;
743 /* Entropy: 256 random bits */
744 if (!wcd->entropy.buffer)
745 sipe_tls_fill_random(&wcd->entropy, 256);
747 if (wcd->webticket_negotiate_uri) {
748 /* Try Negotiate authentication first */
750 success = sipe_svc_webticket(sipe_private,
751 wcd->session,
752 wcd->webticket_negotiate_uri,
753 NULL,
754 wcd->service_auth_uri,
755 &wcd->entropy,
756 webticket_token,
757 wcd);
758 wcd->token_state = TOKEN_STATE_SERVICE;
759 } else {
760 success = initiate_fedbearer(sipe_private,
761 wcd);
764 if (success) {
765 /* callback data passed down the line */
766 wcd = NULL;
771 if (wcd) {
772 callback_execute(sipe_private,
773 wcd,
774 uri,
775 NULL,
776 NULL);
777 callback_data_free(wcd);
781 static void service_metadata(struct sipe_core_private *sipe_private,
782 const gchar *uri,
783 SIPE_UNUSED_PARAMETER const gchar *raw,
784 sipe_xml *metadata,
785 gpointer callback_data)
787 struct webticket_callback_data *wcd = callback_data;
789 if (metadata) {
790 const sipe_xml *node;
791 gchar *policy = g_strdup_printf("%s_policy", wcd->service_port);
792 gchar *ticket_uri = NULL;
794 SIPE_DEBUG_INFO("webservice_metadata: metadata for service %s retrieved successfully",
795 uri);
797 /* WebTicket policies accepted by Web Service */
798 for (node = sipe_xml_child(metadata, "Policy");
799 node;
800 node = sipe_xml_twin(node)) {
801 if (sipe_strcase_equal(sipe_xml_attribute(node, "Id"),
802 policy)) {
804 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: WebTicket policy found");
806 ticket_uri = sipe_xml_data(sipe_xml_child(node,
807 "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address"));
808 if (ticket_uri) {
809 /* this token type requires signing */
810 wcd->requires_signing = TRUE;
811 } else {
812 /* try alternative token type */
813 ticket_uri = sipe_xml_data(sipe_xml_child(node,
814 "ExactlyOne/All/SignedSupportingTokens/Policy/IssuedToken/Issuer/Address"));
816 if (ticket_uri) {
817 SIPE_DEBUG_INFO("webservice_metadata: WebTicket URI %s", ticket_uri);
819 break;
822 g_free(policy);
824 if (ticket_uri) {
826 /* Authentication ports accepted by Web Service */
827 for (node = sipe_xml_child(metadata, "service/port");
828 node;
829 node = sipe_xml_twin(node)) {
830 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
831 wcd->service_port)) {
832 const gchar *auth_uri;
834 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: authentication port found");
836 auth_uri = sipe_xml_attribute(sipe_xml_child(node,
837 "address"),
838 "location");
839 if (auth_uri) {
840 SIPE_DEBUG_INFO("webservice_metadata: Auth URI %s", auth_uri);
842 if (sipe_svc_metadata(sipe_private,
843 wcd->session,
844 ticket_uri,
845 webticket_metadata,
846 wcd)) {
847 /* Remember for later */
848 wcd->service_auth_uri = g_strdup(auth_uri);
850 /* callback data passed down the line */
851 wcd = NULL;
854 break;
857 g_free(ticket_uri);
861 if (wcd) {
862 callback_execute(sipe_private,
863 wcd,
864 uri,
865 NULL,
866 NULL);
867 callback_data_free(wcd);
871 gboolean sipe_webticket_request(struct sipe_core_private *sipe_private,
872 struct sipe_svc_session *session,
873 const gchar *base_uri,
874 const gchar *port_name,
875 sipe_webticket_callback *callback,
876 gpointer callback_data)
878 struct sipe_webticket *webticket;
879 gboolean ret = FALSE;
881 sipe_webticket_init(sipe_private);
882 webticket = sipe_private->webticket;
884 if (webticket->shutting_down) {
885 SIPE_DEBUG_ERROR("sipe_webticket_request: new Web Ticket request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
886 "Base URI: %s\n"
887 "Port Name: %s\n",
888 base_uri,
889 port_name);
891 } else {
892 const struct webticket_token *wt = cache_hit(sipe_private, base_uri);
894 /* cache hit for this URI? */
895 if (wt) {
896 SIPE_DEBUG_INFO("sipe_webticket_request: using cached token for URI %s (Auth URI %s)",
897 base_uri, wt->auth_uri);
898 callback(sipe_private,
899 base_uri,
900 wt->auth_uri,
901 wt->token,
902 NULL,
903 callback_data);
904 ret = TRUE;
905 } else {
906 GHashTable *pending = webticket->pending;
907 struct webticket_callback_data *wcd = g_hash_table_lookup(pending,
908 base_uri);
910 /* is there already a pending request for this URI? */
911 if (wcd) {
912 SIPE_DEBUG_INFO("sipe_webticket_request: pending request found for URI %s - queueing",
913 base_uri);
914 queue_request(wcd, callback, callback_data);
915 ret = TRUE;
916 } else {
917 wcd = g_new0(struct webticket_callback_data, 1);
919 ret = sipe_svc_metadata(sipe_private,
920 session,
921 base_uri,
922 service_metadata,
923 wcd);
925 if (ret) {
926 wcd->service_uri = g_strdup(base_uri);
927 wcd->service_port = port_name;
928 wcd->callback = callback;
929 wcd->callback_data = callback_data;
930 wcd->session = session;
931 wcd->token_state = TOKEN_STATE_NONE;
932 g_hash_table_insert(pending,
933 wcd->service_uri, /* borrowed */
934 wcd); /* borrowed */
935 } else {
936 g_free(wcd);
942 return(ret);
946 Local Variables:
947 mode: c
948 c-file-style: "bsd"
949 indent-tabs-mode: t
950 tab-width: 8
951 End: