OBS: sync with latest versions
[siplcs.git] / src / core / sipe-webticket.c
blobf17abf5a1ff68a758bab49e41c2539caa44c5580
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);
291 /* try alternative names */
292 if (!timestamp)
293 timestamp = generate_timestamp(raw, "wst:Lifetime");
294 if (!keydata)
295 keydata = sipe_xml_extract_raw(raw, "saml1:Assertion", TRUE);
297 /* clear old ADFS token */
298 g_free(webticket->adfs_token);
299 webticket->adfs_token = NULL;
301 if (timestamp && keydata) {
302 gchar *expires_string = sipe_xml_extract_raw(timestamp,
303 "wsu:Expires",
304 FALSE);
306 if (expires_string) {
308 SIPE_DEBUG_INFO("generate_federation_wsse: found timestamp & keydata, expires %s",
309 expires_string);
311 /* cache ADFS token */
312 webticket->adfs_token = g_strconcat(timestamp,
313 keydata,
314 NULL);
315 webticket->adfs_token_expires = sipe_utils_str_to_time(expires_string);
316 g_free(expires_string);
320 g_free(keydata);
321 g_free(timestamp);
324 static gchar *generate_sha1_proof_wsse(const gchar *raw,
325 struct sipe_tls_random *entropy,
326 time_t *expires)
328 gchar *timestamp = generate_timestamp(raw, "Lifetime");
329 gchar *keydata = sipe_xml_extract_raw(raw, "saml:Assertion", TRUE);
330 gchar *wsse_security = NULL;
332 if (timestamp && keydata) {
333 gchar *expires_string = sipe_xml_extract_raw(timestamp,
334 "Expires",
335 FALSE);
337 if (entropy) {
338 gchar *assertionID = extract_raw_xml_attribute(keydata,
339 "AssertionID");
342 * WS-Trust 1.3
344 * http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1:
346 * "The key is computed using P_SHA1() from the TLS sepcification to generate
347 * a bit stream using entropy from both sides. The exact form is:
349 * key = P_SHA1(Entropy_REQ, Entropy_RES)"
351 gchar *entropy_res_base64 = sipe_xml_extract_raw(raw, "BinarySecret", FALSE);
352 gsize entropy_res_length;
353 guchar *entropy_response = g_base64_decode(entropy_res_base64,
354 &entropy_res_length);
355 guchar *key = sipe_tls_p_sha1(entropy->buffer,
356 entropy->length,
357 entropy_response,
358 entropy_res_length,
359 entropy->length);
360 g_free(entropy_response);
361 g_free(entropy_res_base64);
363 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata");
365 if (assertionID && key) {
366 /* same as SIPE_DIGEST_HMAC_SHA1_LENGTH */
367 guchar digest[SIPE_DIGEST_SHA1_LENGTH];
368 gchar *base64;
369 gchar *signed_info;
370 gchar *canon;
372 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found assertionID and successfully computed the key");
374 /* Digest over reference element (#timestamp -> wsu:Timestamp) */
375 sipe_digest_sha1((guchar *) timestamp,
376 strlen(timestamp),
377 digest);
378 base64 = g_base64_encode(digest,
379 SIPE_DIGEST_SHA1_LENGTH);
381 /* XML-Sig: SignedInfo for reference element */
382 signed_info = g_strdup_printf("<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
383 "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
384 "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"/>"
385 "<Reference URI=\"#timestamp\">"
386 "<Transforms>"
387 "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>"
388 "</Transforms>"
389 "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>"
390 "<DigestValue>%s</DigestValue>"
391 "</Reference>"
392 "</SignedInfo>",
393 base64);
394 g_free(base64);
396 /* XML-Sig: SignedInfo in canonical form */
397 canon = sipe_xml_exc_c14n(signed_info);
398 g_free(signed_info);
400 if (canon) {
401 gchar *signature;
403 /* calculate signature */
404 sipe_digest_hmac_sha1(key, entropy->length,
405 (guchar *)canon,
406 strlen(canon),
407 digest);
408 base64 = g_base64_encode(digest,
409 SIPE_DIGEST_HMAC_SHA1_LENGTH);
411 /* XML-Sig: Signature from SignedInfo + Key */
412 signature = g_strdup_printf("<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"
413 " %s"
414 " <SignatureValue>%s</SignatureValue>"
415 " <KeyInfo>"
416 " <wsse:SecurityTokenReference wsse:TokenType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1\">"
417 " <wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID\">%s</wsse:KeyIdentifier>"
418 " </wsse:SecurityTokenReference>"
419 " </KeyInfo>"
420 "</Signature>",
421 canon,
422 base64,
423 assertionID);
424 g_free(base64);
425 g_free(canon);
427 wsse_security = g_strconcat(timestamp,
428 keydata,
429 signature,
430 NULL);
431 g_free(signature);
436 g_free(key);
437 g_free(assertionID);
438 } else {
439 /* token doesn't require signature */
440 SIPE_DEBUG_INFO_NOFORMAT("generate_sha1_proof_wsse: found timestamp & keydata, no signing required");
441 wsse_security = g_strconcat(timestamp,
442 keydata,
443 NULL);
446 *expires = 0;
447 if (expires_string) {
448 *expires = sipe_utils_str_to_time(expires_string);
449 g_free(expires_string);
453 g_free(keydata);
454 g_free(timestamp);
455 return(wsse_security);
458 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
459 struct webticket_callback_data *wcd);
460 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
461 struct webticket_callback_data *wcd);
462 static void webticket_token(struct sipe_core_private *sipe_private,
463 const gchar *uri,
464 const gchar *raw,
465 sipe_xml *soap_body,
466 gpointer callback_data)
468 struct webticket_callback_data *wcd = callback_data;
469 gboolean failed = TRUE;
471 if (soap_body) {
472 switch (wcd->token_state) {
473 case TOKEN_STATE_NONE:
474 SIPE_DEBUG_INFO_NOFORMAT("webticket_token: ILLEGAL STATE - should not happen...");
475 break;
477 case TOKEN_STATE_SERVICE: {
478 /* WebTicket for Web Service */
479 time_t expires;
480 gchar *wsse_security = generate_sha1_proof_wsse(raw,
481 wcd->requires_signing ? &wcd->entropy : NULL,
482 &expires);
484 if (wsse_security) {
485 /* cache takes ownership of wsse_security */
486 cache_token(sipe_private,
487 wcd->service_uri,
488 wcd->service_auth_uri,
489 wsse_security,
490 expires);
491 callback_execute(sipe_private,
492 wcd,
493 wcd->service_auth_uri,
494 wsse_security,
495 NULL);
496 failed = FALSE;
498 break;
501 case TOKEN_STATE_FEDERATION:
502 /* WebTicket from ADFS for federated authentication */
503 generate_federation_wsse(sipe_private->webticket,
504 raw);
506 if (sipe_private->webticket->adfs_token) {
508 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from ADFS %s",
509 uri);
511 if (federated_authentication(sipe_private,
512 wcd)) {
513 /* callback data passed down the line */
514 wcd = NULL;
517 break;
519 case TOKEN_STATE_FED_BEARER: {
520 /* WebTicket for federated authentication */
521 gchar *wsse_security = generate_fedbearer_wsse(raw);
523 if (wsse_security) {
525 SIPE_DEBUG_INFO("webticket_token: received valid SOAP message from service %s",
526 uri);
528 if (sipe_svc_webticket(sipe_private,
529 wcd->session,
530 wcd->webticket_fedbearer_uri,
531 wsse_security,
532 wcd->service_auth_uri,
533 &wcd->entropy,
534 webticket_token,
535 wcd)) {
536 wcd->token_state = TOKEN_STATE_SERVICE;
538 /* callback data passed down the line */
539 wcd = NULL;
541 g_free(wsse_security);
543 break;
546 /* end of: switch (wcd->token_state) { */
549 } else if (uri) {
550 /* Retry with federated authentication? */
551 if (wcd->webticket_fedbearer_uri && !wcd->tried_fedbearer) {
552 SIPE_DEBUG_INFO("webticket_token: anonymous authentication to service %s failed, retrying with federated authentication",
553 uri);
555 if (initiate_fedbearer(sipe_private, wcd)) {
556 /* callback data passed down the line */
557 wcd = NULL;
562 if (wcd) {
563 if (failed) {
564 gchar *failure_msg = NULL;
566 if (soap_body) {
567 failure_msg = sipe_xml_data(sipe_xml_child(soap_body,
568 "Body/Fault/Detail/error/internalerror/text"));
569 /* XML data can end in &#x000D;&#x000A; */
570 g_strstrip(failure_msg);
573 callback_execute(sipe_private,
574 wcd,
575 uri,
576 NULL,
577 failure_msg);
578 g_free(failure_msg);
580 callback_data_free(wcd);
584 static gboolean federated_authentication(struct sipe_core_private *sipe_private,
585 struct webticket_callback_data *wcd)
587 gboolean success;
589 if ((success = sipe_svc_webticket_lmc_federated(sipe_private,
590 wcd->session,
591 sipe_private->webticket->adfs_token,
592 wcd->webticket_fedbearer_uri,
593 webticket_token,
594 wcd)))
595 wcd->token_state = TOKEN_STATE_FED_BEARER;
597 /* If TRUE then callback data has been passed down the line */
598 return(success);
601 static gboolean fedbearer_authentication(struct sipe_core_private *sipe_private,
602 struct webticket_callback_data *wcd)
604 struct sipe_webticket *webticket = sipe_private->webticket;
605 gboolean success;
607 /* make sure a cached ADFS token is still valid for 60 seconds */
608 if (webticket->adfs_token &&
609 (webticket->adfs_token_expires >= time(NULL) + 60)) {
611 SIPE_DEBUG_INFO_NOFORMAT("fedbearer_authentication: reusing cached ADFS token");
612 success = federated_authentication(sipe_private, wcd);
614 } else if (webticket->webticket_adfs_uri) {
615 if ((success = sipe_svc_webticket_adfs(sipe_private,
616 wcd->session,
617 webticket->webticket_adfs_uri,
618 webticket_token,
619 wcd)))
620 wcd->token_state = TOKEN_STATE_FEDERATION;
621 } else {
622 if ((success = sipe_svc_webticket_lmc(sipe_private,
623 wcd->session,
624 wcd->webticket_fedbearer_uri,
625 webticket_token,
626 wcd)))
627 wcd->token_state = TOKEN_STATE_FED_BEARER;
630 /* If TRUE then callback data has been passed down the line */
631 return(success);
634 static void realminfo(struct sipe_core_private *sipe_private,
635 const gchar *uri,
636 SIPE_UNUSED_PARAMETER const gchar *raw,
637 sipe_xml *realminfo,
638 gpointer callback_data)
640 struct sipe_webticket *webticket = sipe_private->webticket;
641 struct webticket_callback_data *wcd = callback_data;
643 /* Only try retrieving of RealmInfo once */
644 webticket->retrieved_realminfo = TRUE;
647 * We must specifically check for abort, because
648 * realminfo == NULL is a valid response
650 if (uri) {
651 if (realminfo) {
652 /* detect ADFS setup. See also:
654 * http://en.wikipedia.org/wiki/Active_Directory_Federation_Services
656 * NOTE: this is based on observed behaviour.
657 * It is unkown if this is documented somewhere...
659 SIPE_DEBUG_INFO("realminfo: data for user %s retrieved successfully",
660 sipe_private->username);
662 webticket->webticket_adfs_uri = sipe_xml_data(sipe_xml_child(realminfo,
663 "STSAuthURL"));
666 if (webticket->webticket_adfs_uri)
667 SIPE_DEBUG_INFO("realminfo: ADFS setup detected: %s",
668 webticket->webticket_adfs_uri);
669 else
670 SIPE_DEBUG_INFO_NOFORMAT("realminfo: no RealmInfo found or no ADFS setup detected - try direct login");
672 if (fedbearer_authentication(sipe_private, wcd)) {
673 /* callback data passed down the line */
674 wcd = NULL;
678 if (wcd) {
679 callback_execute(sipe_private,
680 wcd,
681 uri,
682 NULL,
683 NULL);
684 callback_data_free(wcd);
688 static gboolean initiate_fedbearer(struct sipe_core_private *sipe_private,
689 struct webticket_callback_data *wcd)
691 gboolean success;
693 if (sipe_private->webticket->retrieved_realminfo) {
694 /* skip retrieval and go to authentication */
695 success = fedbearer_authentication(sipe_private, wcd);
696 } else {
697 success = sipe_svc_realminfo(sipe_private,
698 wcd->session,
699 realminfo,
700 wcd);
703 wcd->tried_fedbearer = TRUE;
705 return(success);
708 static void webticket_metadata(struct sipe_core_private *sipe_private,
709 const gchar *uri,
710 SIPE_UNUSED_PARAMETER const gchar *raw,
711 sipe_xml *metadata,
712 gpointer callback_data)
714 struct webticket_callback_data *wcd = callback_data;
716 if (metadata) {
717 const sipe_xml *node;
719 SIPE_DEBUG_INFO("webticket_metadata: metadata for service %s retrieved successfully",
720 uri);
722 /* Authentication ports accepted by WebTicket Service */
723 for (node = sipe_xml_child(metadata, "service/port");
724 node;
725 node = sipe_xml_twin(node)) {
726 const gchar *auth_uri = sipe_xml_attribute(sipe_xml_child(node,
727 "address"),
728 "location");
730 if (auth_uri) {
731 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
732 "WebTicketServiceWinNegotiate")) {
733 SIPE_DEBUG_INFO("webticket_metadata: WebTicket Windows Negotiate Auth URI %s", auth_uri);
734 g_free(wcd->webticket_negotiate_uri);
735 wcd->webticket_negotiate_uri = g_strdup(auth_uri);
736 } else if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
737 "WsFedBearer")) {
738 SIPE_DEBUG_INFO("webticket_metadata: WebTicket FedBearer Auth URI %s", auth_uri);
739 g_free(wcd->webticket_fedbearer_uri);
740 wcd->webticket_fedbearer_uri = g_strdup(auth_uri);
745 if (wcd->webticket_negotiate_uri || wcd->webticket_fedbearer_uri) {
746 gboolean success;
748 /* Entropy: 256 random bits */
749 if (!wcd->entropy.buffer)
750 sipe_tls_fill_random(&wcd->entropy, 256);
752 if (wcd->webticket_negotiate_uri) {
753 /* Try Negotiate authentication first */
755 success = sipe_svc_webticket(sipe_private,
756 wcd->session,
757 wcd->webticket_negotiate_uri,
758 NULL,
759 wcd->service_auth_uri,
760 &wcd->entropy,
761 webticket_token,
762 wcd);
763 wcd->token_state = TOKEN_STATE_SERVICE;
764 } else {
765 success = initiate_fedbearer(sipe_private,
766 wcd);
769 if (success) {
770 /* callback data passed down the line */
771 wcd = NULL;
776 if (wcd) {
777 callback_execute(sipe_private,
778 wcd,
779 uri,
780 NULL,
781 NULL);
782 callback_data_free(wcd);
786 static void service_metadata(struct sipe_core_private *sipe_private,
787 const gchar *uri,
788 SIPE_UNUSED_PARAMETER const gchar *raw,
789 sipe_xml *metadata,
790 gpointer callback_data)
792 struct webticket_callback_data *wcd = callback_data;
794 if (metadata) {
795 const sipe_xml *node;
796 gchar *policy = g_strdup_printf("%s_policy", wcd->service_port);
797 gchar *ticket_uri = NULL;
799 SIPE_DEBUG_INFO("webservice_metadata: metadata for service %s retrieved successfully",
800 uri);
802 /* WebTicket policies accepted by Web Service */
803 for (node = sipe_xml_child(metadata, "Policy");
804 node;
805 node = sipe_xml_twin(node)) {
806 if (sipe_strcase_equal(sipe_xml_attribute(node, "Id"),
807 policy)) {
809 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: WebTicket policy found");
811 ticket_uri = sipe_xml_data(sipe_xml_child(node,
812 "ExactlyOne/All/EndorsingSupportingTokens/Policy/IssuedToken/Issuer/Address"));
813 if (ticket_uri) {
814 /* this token type requires signing */
815 wcd->requires_signing = TRUE;
816 } else {
817 /* try alternative token type */
818 ticket_uri = sipe_xml_data(sipe_xml_child(node,
819 "ExactlyOne/All/SignedSupportingTokens/Policy/IssuedToken/Issuer/Address"));
821 if (ticket_uri) {
822 SIPE_DEBUG_INFO("webservice_metadata: WebTicket URI %s", ticket_uri);
824 break;
827 g_free(policy);
829 if (ticket_uri) {
831 /* Authentication ports accepted by Web Service */
832 for (node = sipe_xml_child(metadata, "service/port");
833 node;
834 node = sipe_xml_twin(node)) {
835 if (sipe_strcase_equal(sipe_xml_attribute(node, "name"),
836 wcd->service_port)) {
837 const gchar *auth_uri;
839 SIPE_DEBUG_INFO_NOFORMAT("webservice_metadata: authentication port found");
841 auth_uri = sipe_xml_attribute(sipe_xml_child(node,
842 "address"),
843 "location");
844 if (auth_uri) {
845 SIPE_DEBUG_INFO("webservice_metadata: Auth URI %s", auth_uri);
847 if (sipe_svc_metadata(sipe_private,
848 wcd->session,
849 ticket_uri,
850 webticket_metadata,
851 wcd)) {
852 /* Remember for later */
853 wcd->service_auth_uri = g_strdup(auth_uri);
855 /* callback data passed down the line */
856 wcd = NULL;
859 break;
862 g_free(ticket_uri);
866 if (wcd) {
867 callback_execute(sipe_private,
868 wcd,
869 uri,
870 NULL,
871 NULL);
872 callback_data_free(wcd);
876 gboolean sipe_webticket_request(struct sipe_core_private *sipe_private,
877 struct sipe_svc_session *session,
878 const gchar *base_uri,
879 const gchar *port_name,
880 sipe_webticket_callback *callback,
881 gpointer callback_data)
883 struct sipe_webticket *webticket;
884 gboolean ret = FALSE;
886 sipe_webticket_init(sipe_private);
887 webticket = sipe_private->webticket;
889 if (webticket->shutting_down) {
890 SIPE_DEBUG_ERROR("sipe_webticket_request: new Web Ticket request during shutdown: THIS SHOULD NOT HAPPEN! Debugging information:\n"
891 "Base URI: %s\n"
892 "Port Name: %s\n",
893 base_uri,
894 port_name);
896 } else {
897 const struct webticket_token *wt = cache_hit(sipe_private, base_uri);
899 /* cache hit for this URI? */
900 if (wt) {
901 SIPE_DEBUG_INFO("sipe_webticket_request: using cached token for URI %s (Auth URI %s)",
902 base_uri, wt->auth_uri);
903 callback(sipe_private,
904 base_uri,
905 wt->auth_uri,
906 wt->token,
907 NULL,
908 callback_data);
909 ret = TRUE;
910 } else {
911 GHashTable *pending = webticket->pending;
912 struct webticket_callback_data *wcd = g_hash_table_lookup(pending,
913 base_uri);
915 /* is there already a pending request for this URI? */
916 if (wcd) {
917 SIPE_DEBUG_INFO("sipe_webticket_request: pending request found for URI %s - queueing",
918 base_uri);
919 queue_request(wcd, callback, callback_data);
920 ret = TRUE;
921 } else {
922 wcd = g_new0(struct webticket_callback_data, 1);
924 ret = sipe_svc_metadata(sipe_private,
925 session,
926 base_uri,
927 service_metadata,
928 wcd);
930 if (ret) {
931 wcd->service_uri = g_strdup(base_uri);
932 wcd->service_port = port_name;
933 wcd->callback = callback;
934 wcd->callback_data = callback_data;
935 wcd->session = session;
936 wcd->token_state = TOKEN_STATE_NONE;
937 g_hash_table_insert(pending,
938 wcd->service_uri, /* borrowed */
939 wcd); /* borrowed */
940 } else {
941 g_free(wcd);
947 return(ret);
951 Local Variables:
952 mode: c
953 c-file-style: "bsd"
954 indent-tabs-mode: t
955 tab-width: 8
956 End: